117

I have below annotation.

MyAnnotation.java

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

}

SomeAspect.java

public class SomeAspect{

 @Around("execution(public * *(..)) && @annotation(com.mycompany.MyAnnotation)")
    public Object procede(ProceedingJoinPoint call) throws Throwable {

  //Some logic

}

}

SomeOther.java

public class SomeOther{

@MyAnnotation("ABC") 
public String someMethod(String name){


}


}

In above class am passing "ABC" with in @MyAnnotation. Now how can i access "ABC" value in procede method of SomeAspect.java class?

Thanks!

user755806
  • 6,565
  • 27
  • 106
  • 153

6 Answers6

205

You can get the Signature from a ProceedingJoinPoint and in case of a method invocation just cast it to a MethodSignature.

@Around("execution(public * *(..)) && @annotation(com.mycompany.MyAnnotation)")
public Object procede(ProceedingJoinPoint call) throws Throwable {
    MethodSignature signature = (MethodSignature) call.getSignature();
    Method method = signature.getMethod();

    MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
}

But you should first add an annotation attribute. Your example code doesn't have one, e.g.

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

    String value();
}

Then you can access it

MyAnnotation myAnnotation = method.getAnnotation(MyAnnotation.class);
String value = myAnnotation.value();

EDIT

How to get value if I have @MyAnnotation("ABC") at class level ?

A Class is also an AnnotatedElement, so you can get it the same way as from a Method. E.g. An annotation of the method's declaring class can be obtained using

 Method method = ...;
 Class<?> declaringClass = method.getDeclaringClass();
 MyAnnotation myAnnotation = declaringClass.getAnnotation(MyAnnotation.class)

Since you are using spring you might also want to use spring's AnnotationUtils.findAnnotation(..). It searches for an annotation as spring does. E.g. also looking at superclass and interface methods, etc.

 MyAnnotation foundAnnotation = AnnotationUtils.findAnnotation(method, MyAnnotation.class);

EDIT

You might also be interessted in the capabilities of spring's MergedAnnotations which was introduced in 5.2.

René Link
  • 48,224
  • 13
  • 108
  • 140
49

Actually I think we can get the value in another way round instead of just from ProceedingJoinPoint, which will definitely require us to make use of reflection.

Have a try as follows using annotation directly: add com.mycompany.MyAnnotation yourAnnotation in your advice params and @annotation(yourAnnotation) in @Around.

@Around("execution(public * *(..)) && @annotation(yourAnnotation)")
public Object procede(ProceedingJoinPoint pjp, com.mycompany.MyAnnotation yourAnnotation) {
    ...
    yourAnnotation.value(); // get your annotation value directly;
    ...
}

com.mycompany.MyAnnotation in advice params just work as that in

@Around("execution(public * *(..)) && @annotation(com.mycompany.MyAnnotation)")

yourAnnotation can be valid variable name since MyAnnotation in params already points out which annotation it should be. Here yourAnnotation is used to retrieve the annotation instance only.

If you want to pass more params you can try args().

For more details, do please check its official doc. For Annotation value, you can just search @Auditable.

Hearen
  • 7,420
  • 4
  • 53
  • 63
  • 9
    I think this should be the accepted answer, as it is cleaner and the documented way: https://docs.spring.io/spring/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html – franDayz Dec 18 '18 at 09:22
  • 1
    I agree (should be accepted answer), this is definitely the way to go. Accepted answer, although correct, is very clunky. – Paul A. Trzyna Jul 01 '19 at 19:11
  • Will this work with plain AspectJ or only with Spring-AOP? – Jakub Bochenski Aug 20 '19 at 14:45
  • 1
    @JakubBochenski As far as I know, Spring AOP is built upon AspectJ, and so basically if Spring AOP can do the trick, AspectJ should support it in some way. But I have not tried it myself ;p – Hearen Aug 29 '19 at 23:20
  • "Should" is a big word here... fortunately I've checked this in practice and it does work – Jakub Bochenski Sep 04 '19 at 11:21
  • 1
    As mentioned above by other users, the accepted answer works but I think this should be the accepted answer because is straightforward and officially documented, exactly in this section: https://docs.spring.io/spring/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html#aop-ataspectj-advice-params-names. – ame-h Sep 21 '19 at 04:02
3

This works as well - You can fetch annotation information using reflection on the class.

Annotation anno = MyClass.class.getAnnotation(MyAnnotation.class);

Or

Annotation anno = MyClass.class.getDeclaredMethod("somethod").getAnnotation(MyAnnotation.class);

This works only if your annotation is available at runtime, which you have declared correctly.

@Retention(RetentionPolicy.RUNTIME)
Hinotori
  • 552
  • 6
  • 15
Martin Frey
  • 10,025
  • 4
  • 25
  • 30
  • which Annotation class do you use, there are more than one – JesseBoyd Feb 20 '18 at 00:40
  • The trick is to declare a @PointCut with the annotation like: @Pointcut("@annotation(com.mycompany.MyAnnotation))") public void myAnnotation() {} and then reference it in your advice like so: @Before("@annotation(myAnnotation)") public void beforeSqsListener(com.mycompany.MyAnnotation myAnnotation) { // use myAnnotation like a normal variable... } – Christian von Wendt-Jensen Apr 22 '23 at 05:58
  • Sorry about the "code blocks" in previous comment. I'm simply not able to markup code blocks. – Christian von Wendt-Jensen Apr 22 '23 at 06:04
1

René's example is taking me a long way. Also the explanation how I get ClassLevel Annotations.

But then I can only read ClassLevel Annotations Values if I have previously used a method annotation with "*@Around("execution(public * (..)) && @annotation(com.mycompany.MyAnnotation)")""

How can I get around this? How can I trigger an Aspect if a ClassLevel Annotation is set without going through a Method Execution?

I want to write a ClassLevel Annotation like

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(value = { ElementType.METHOD, ElementType.TYPE })
@EnableSwagger2
@Import(SwaggerConfiguration.class)
public @interface EnableSwaggerApi {
    String controllerPackage() default "foo.bar.ctrl";
}

It's importing the Configuration about "SwaggerConfiguration" where I want to receive the value of "controllerPackage"

@Aspect
public class SwaggerConfiguration {

    @Value("${tom.swagger.controller.package:foo.bar.notset}")
    private String  controllerPackage;

    @Value("${tom.swagger.api.version:1.0.0}")
    private String  apiVersion;

    @Value("${spring.application.name:MyApplication}")
    private String  applicationName;

    @Around("execution(public * *(..)) && @annotation(EnableSwaggerApi)")
    public void procede(ProceedingJoinPoint call) throws Throwable {
        MethodSignature signature = (MethodSignature) call.getSignature();
        Method method = signature.getMethod();

        Class<?> declaringClass = method.getDeclaringClass();
        EnableSwaggerApi myAnnotation = declaringClass.getAnnotation(EnableSwaggerApi.class);
        System.err.println("1 -> " + myAnnotation.controllerPackage());  // -> tko.backend.spring.ctrl

        myAnnotation = method.getAnnotation(EnableSwaggerApi.class);
        System.err.println("2 -> " + myAnnotation.controllerPackage()); // -> tko.backend.spring.SOMEOTHERSTUFF


        // THIS WORKS, BUT JUST IF I USE THE @EnableSwaggerApi ON SOME METHOD!
        // NOT ON CLASS

    }

    @Bean
    public Docket swaggerApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("controllerPackage"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(new ApiInfoBuilder().version(apiVersion).title(applicationName).description("Documentation " + applicationName + " API v" + apiVersion)
                        .build());
    }

    @Bean
    public CorsFilter corsFilter() {
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        final CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/v2/api-docs", config);
        return new CorsFilter(source);
    }
}





@EnableSwaggerApi(controllerPackage="tko.backend.spring.ctrl")
public class Application extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(Application.class, Initializer.class);
    }


    @Bean
    @EnableSwaggerApi(controllerPackage="tko.backend.spring.SOMEOTHERSTUFF")
    public String initSwagger() {
        return "some dummy";
    }

}

How can I can rid of the annotation on initSwagger()? Since the Application.class is not known to SwaggerConfiguration (Swagger Stuff it's in a separate lib) I can't use simple reflection like

Application.class.getAnnotation(EnableSwaggerApi.class)
Tom K.
  • 51
  • 9
0

Find working code for Method Annotation and class level annotation using AspectJ/AOP

   @Around("execution(* com.first.test.controller..*(..)))")
    public Object profileAllMethods(ProceedingJoinPoint proceedingJoinPoint) throws Throwable
    {
        MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();

        java.lang.reflect.Method method = methodSignature.getMethod();

  Annotation []methodAnnotations =  method.getDeclaredAnnotations();
        System.out.println("==============="+methodAnnotations[0].toString());

        Annotation []classAnnotations = proceedingJoinPoint.getTarget().getClass().getAnnotations();

        System.out.println("===Class Annotation : "+classAnnotations[1].toString());
       Object result = proceedingJoinPoint.proceed();
        return result;
    }
Rajeev Rathor
  • 1,830
  • 25
  • 20
0

You can simply bind the method's as described in the docs.

MyAnnotation.java

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

}

SomeAspect.java

public class SomeAspect{

 @Around("execution(public * *(..)) && @annotation(myAnnotation)")
    public Object procede(ProceedingJoinPoint call, MyAnnotation myAnnotation) throws Throwable {

  //Some logic

  }
}
Jefferson Lima
  • 5,186
  • 2
  • 28
  • 28
  • This solution doesn't work, a method annotated with @Around must have only one parameter of type `ProceedingJoinPoint`. – Michaël COLL Jan 09 '23 at 14:00