简体   繁体   中英

Create Pointcut for Spring AOP annotation to put annotation on class and execute advice on every method in class

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---");
    }
}

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);
}

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 (ie 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)")

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM