Search code examples
javaspringspring-bootspring-aopjava-annotations

In Spring Boot AOP, how to get class annotation when in the method "Around" period?


Thanks for all the friends here. I know how to get the annotation parameter for the annotation set on method.

While I would like to add the annotation in the Class level. How to retrieve the attribute from Class in the AOP Around method?

By the way, I have another question, why I need to change the response value from void to Object and return pjp.proceed(). If the method do not have response, the request will freezing.

For the Method which has HasRole annotation

@Around("@annotation(com.example.demo.aspect.annotation.HasRole) && @annotation(annotation)")
public void aroundHasRole(ProceedingJoinPoint pjp, HasRole annotation) throws Throwable {

    log.info("value->>{}", annotation.value());
    pjp.proceed();

}

For Class or Method which has HasRole annotation

@Around("(@within(com.example.demo.aspect.annotation.IsAdmin)"
        + "|| @annotation(com.example.demo.aspect.annotation.IsAdmin))")
public Object aroundHasRole(ProceedingJoinPoint pjp) throws Throwable {

    <<How to get annotation information here?>>
    return pjp.proceed();

}


Solution

  • As you probably have noticed, you cannot bind information from different || branches to an advice method parameter because it would be ambiguous, see also my answers here and here. So if you want to avoid ugly (and slow) reflection, do what I recommended in the other answers and write two distinct advices, factoring out the common code into a helper method if avoiding code duplication is your concern here. Something like this (untested, just to give you an idea about the code structure):

    @Around("@within(annotation)")
    public Object classIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
      return commonIsAdmin(pjp, annotation);
    }
    
    @Around("@annotation(annotation)")
    public Object methodIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
      return commonIsAdmin(pjp, annotation);
    }
    
    public Object commonIsAdmin(ProceedingJoinPoint pjp, IsAdmin annotation) throws Throwable {
      // Here you would place all common advice code.
      // Non-common code could remain in the original advice methods.
      log.info("value->>{}", annotation.value());
      return pjp.proceed();
    }
    

    why I need to change the response value from void to Object and return pjp.proceed(). If the method do not have response, the request will freezing.

    In contrast to a @Before or @After advice, in an @Around advice you can modify the return value by either skipping the target method execution completely by not calling proceed() or by discarding or modifying the result of proceed(). You are completely free in what you want to return, it just has to match the target method's return type.

    Having said that, it should become clear that an around advice method also must have a return type matching that of the target method(s) it intercepts. It can be an exact type like MyType, a super type or simply Object (super type for all types) if your advice targets a multitude of types without a common super type. The advice can also have a return type of void if (and only if) all target methods also return void (otherwise the advice just would not match those methods, even if the pointcut as such would match).

    So if an around advice matches is determined by a combination of the pointcut itself and the return type. You can use that as a tool to limit pointcut matching by defining a specific return type (to which you then would need to cast the return value of proceed() because proceed() always returns an Object).

    BTW, if the target method returns a primitive type like int, boolean etc., then the advice would auto-wrap the result to be an Integer or Boolean.

    You really ought to read Spring AOP and AspectJ manuals or tutorials because I am explaining things here which can be found there.


    Update: The OP asked for documentation concerning parameter binding and a description how names are determined:

    • You can specify an argNames parameter for all advice types, e.g. @Before, @After, @Around.
    • If that annotation parameter is absent, Spring AOP will try to match advice method parameter names via class file debug info, if compiled in. Otherwise matching would fail in this case.
    • When using full AspectJ with compile-time weaving instead of Spring AOP, determining names also works without debug info because the AspectJ compiler can determine the necessary information during compilation.

    All of this is described in the Spring AOP manual.