Search code examples
javaspringannotationsaspectj

How do I perform something on each method call of an annotated method?


I want to write an annotation in Java, which executes something before and after execution of the annotated method, similar to what can be done in Spring with aspects.

I've already tried a Spring aspect, but it only works for Beans (as this guy here mentioned) and I want to stay independent from the Spring framework.

A simple class that writes a String to the console:

public class Main {
    public static void main(String[] args) {
        say("How are you?");
    }

    @Hello
    private static void say(String s) {
        System.out.println(s);
    }
}

The associated annotation:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Hello {}

And I need something like (deduced from the Spring aspect)

public Object process(ProceedingJoinPoint proceedingJoinPoint) {
    System.out.println("Hello");
    Object proceed = null;
    try {
        proceed = proceedingJoinPoint.proceed();
    } catch (Throwable throwable) {
        throwable.printStackTrace();
    }
    System.out.println("world!");
    return proceed;
}

I want the following output:

Hello

How are you?

world!

Edit:

I created the following Aspect (without annotations), but it does not work

@lombok.extern.java.Log
public aspect Log {
    pointcut methodExecuted():execution(* **(..));

    void around(): methodExecuted() {
        log.info("Hello");
        proceed();
        log.info("world!");
    }
}

Where is my mistake?


Solution

  • Assuming that you successfully compiled your aspect with the AspectJ compiler, it should work with the code you used, only that it would log all method executions, i.e. also main(..), so you would se the aspect's output twice before and after "How are you?". If you don't see anything probably you made a mistake in setting up your build system.

    You should change the pointcut to actually limit logging to annotated methods: execution(* *(..)) && @annotation(Hello). Furthermore if your around advice has a void return type, logging will not work with non-void methods. So you should rather use a return type of Object and actually return the result of proceed().

    I also strongly urge you to not just blindly wield a powerful tool like AspectJ but also study some documentation before you do. It is quite obvious that you have not done so or only very cursorily. Then you get the effect of being a not-so-capable-user with a tool. ;-)

    Here is my MCVE:

    package de.scrum_master.app;
    
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    
    import java.lang.annotation.Retention;
    import java.lang.annotation.Target;
    
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface Hello {}
    
    package de.scrum_master.app;
    
    public class Application {
      public static void main(String[] args) {
        say("How are you?");
      }
    
      @Hello
      private static void say(String s) {
        System.out.println(s);
      }
    }
    
    package de.scrum_master.aspect;
    
    import de.scrum_master.app.Hello;
    
    public aspect LoggingAspect {
      pointcut methodExecuted() : execution(* *(..)) && @annotation(Hello);
    
      Object around(): methodExecuted() {
        System.out.println("Hello");
        Object result = proceed();
        System.out.println("world!");
        return result;
      }
    }
    

    Console log:

    Hello
    How are you?
    world!