0
votes

I want to have annotation on class level that will execute advice on every method in annotated class. Is that even possible.

Example: I would like to annotate OmniDemoService with @DoSomethingForMe and I want both method1 and method2 to log "look at me" before execution

This example is not working and I don't know why. When I transform Pointcut to Around and just use it with annotation (also change annotation ElementType to method) everything is working on method level. So I think it is wrong defined Pointcut.

Annotation:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoSomethingForMe {
}

Advice:

@Aspect
@Component
public class DoSomethingForMeAdvice {

    private static final Logger logger = LoggerFactory.getLogger(DoSomethingForMeAdvice.class);

    @Pointcut("execution(public * *(..)) && @annotation(DoSomethingForMe)")
    public void anyAnnotatedMethod() {
    }

    @Before("anyAnnotatedMethod()")
    public void acquireExecution() {
        logger.info("look at me");
    }
}

Usage:

@Service
@DoSomethingForMe
public class OmniDemoService {

    private static final Logger logger = LoggerFactory.getLogger(OmniDemoService.class);

    public void method1() {
            logger.info("---1---");
    }
    
    public void method2() {
            logger.info("---2---");
    }
}
3
yes, that is possible, I don't really understand the actual question. What is the question you are having? - Stultuske
This is not working for me. Probably Pointcut is not defined well. - mladirazvijac
this might be a good read: baeldung.com/spring-aop-annotation - Stultuske
This is just simple explanation of usage of advice on method level with @Around - mladirazvijac
Yes, I know. What is your point? have you tried copying that configuration, changing the target type and changing the location of the annotation? - Stultuske

3 Answers

0
votes

Your issue is that you are confusing pointcut definition with advices.

Pointcut is aiming, advice performs the actual WhateverYouWantToBeExecuted. Like for example

@Pointcut("@annotation(com.omnidemo.advice.DoSomethingForMe)")
public void anyAnnotatedMethod() {
}


@Before("anyAnnotatedMethod()")
public void logMethodCall(JoinPoint jp) {
    String methodName = jp.getSignature().toShortString();
    logger.info("Executing: " + methodName);
}
0
votes

Solution for the problem is to use within for pointcut

@Pointcut("@within(DoSomethingForMe)")
public void anyAnnotatedMethod() {
}

@Before("anyAnnotatedMethod()")
public void acquireExecution() {
    logger.info("look at me");
}

Solution provided by @J Asgarov in the comments

0
votes

Check out what the AspectJ quick reference says about @annotation():

any join point where the subject has an annotation of type SomeAnnotation

You used @annotation(DoSomethingForMe) but the "subject" of a method execution is a method. So that would mean any method annotated @DoSomethingForMe.

Use @this(DoSomethingForMe) or @target(DoSomethingForMe).

Thanks to kriegaex for pointing out that @this and @target must be evaluated at runtime, which would pollute the codebase a lot (i.e. check in every method). So the next approach is better:


If you check the AspectJ manual section about type patterns you will see that you can annotate the type directly. Please also remember to use use fully qualified class names. So that would be:

execution(public * (@com.path.DoSomethingForMe *).*(..))

Also, if you have such a simple pointcut and you don't need to reuse it, I think you can drop the additional method and just use it in the advice directly:

@Before("execution(public * (@com.path.DoSomethingForMe *).*(..))")

which says: "before the execution of any public method of a type annotated with @com.path.DoSomethingForMe", where "before the execution of a method" means "inside the method, at the beginning".


Alternatively, if this pointcut looks a bit too complicated for you, you can separate annotation matching and method matching like this, as suggested by J Asgarov in his comment:

@Before("execution(public * *(..)) && @within(com.path.DoSomethingForMe)")