简体   繁体   中英

How to set a property value using Java custom annotation and Spring AOP?

I would like to use custom Java annotation to insert a value in a private class property using Spring AOP (and/or AspectJ). Quick example:

MyAnnotation.java:

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

MyController.java:

public class MyControllerImpl implements MyController {

    ...
    
    @MyAnnotation
    private String var1;

    @Override
    public String getVarExample() {
       // imagine this is a REST API that gets called on @GET
       // request and returns a string

       System.out.println(this.var1); // <-- I'd like this to be "helloworld"
                                    // this is just for illustration
                                    // of course, I will want to do 
                                    // something more meaningful with
                                    // the 'var1' variable
       return "ok"; <- unimportant for this example
    }
    ...

MyAspect.java:

@Aspect
@Component
public class MyAspect {

    @Pointcut("@annotation(com.mypackage.annotation.MyAnnotation)")
    public void fieldAnnotatedWithMyAnnotation() {
        
    }

    @Around("fieldAnnotatedWithMyAnnotation()")
    public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
        
        // problem #1 - the program never enters here
        // problem #2 - I need to figure out how to set up the var1 here
        //              to "helloworld" , how?
        return pjp.proceed();
    }
    ...
}

What would I like to happen?

I will call and get into the getVarExample() and after it returns I would like to see "helloworld" in console or log. I would like to somehow set the var1 to a custom value using AOP. Any property variable that will be annotated with @MyAnnotation will be set to "helloworld". I hope the example above is clear.

What have I tried?

I made sure there is no typo in the package names, also fiddled with different AOP advice annotations like @Around and @Before . I also tried different targets in the MyAnnotation and ended up with ElementType.FIELD which should be correct.

Can you help me to get it working?

I know this can be done, but couldn't find any working example online. Again, I would like to see 2 answers:

1. How to get the pointcut to trigger on MyController entrance? I want to catch a breakpoint inside the enrichVar1(..) method of the MyAspect class.

2. How can I modify the annotated var1 value in enrichVar1(..) method of the MyAspect class?

I don't know what I am doing wrong. Any help will be greatly appreciated. Thank you!

The AOP is set up correctly in my project. I know that because I am already using AOP for different things (logging for example).

Update #1:

Please, note there are not getters or setters for the var1 private variable. The variable will be only used within the MyControllerImpl . To illustrate this better I changed the return value of the getVarExample .

As it goes from Spring docs Spring AOP does support Spring beans' method execution join points. To make field access join points work you need to use AspectJ's backend with load time weaving for AOP.

But for your case it's not required to use field join points, you can put your annotation on the getter and this should work.

Like I said in my comment:

The pointcut designator @annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set() . Ie, the pointcut would also need to be changed if migrating to AspectJ. But I agree that sticking to Spring AOP and annotating getter methods instead of fields is probably enough here.

But because you insist that you want to keep the controller class unchanged, here is the native AspectJ solution. Please read chapter Using AspectJ with Spring Applications for how to configure that with @EnableLoadTimeWeaving and JVM parameter -javaagent:/path/to/aspectjweaver.jar .

In order to demonstrate that this solution really does work independently of Spring, I am using no Spring classes or annotations at all, only POJOs and native AspectJ. You can simply do the same within your Spring application. Please note that native AspectJ aspects do not need @Component annotations, in contrast to Spring AOP aspects.

package de.scrum_master.app;

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.FIELD })
public @interface MyAnnotation {}
package de.scrum_master.app;

public interface MyController {
  String getVarExample();
}
package de.scrum_master.app;

public class MyControllerImpl implements MyController {
  @MyAnnotation
  private String var1;

  @Override
  public String getVarExample() {
    System.out.println(this.var1);
    return "ok";
  }
}
package de.scrum_master.app;

public class Application {
  public static void main(String[] args) {
    MyController myController = new MyControllerImpl();
    myController.getVarExample();
  }
}
package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {

  @Pointcut("get(@de.scrum_master.app.MyAnnotation * *)")
  public void fieldAnnotatedWithMyAnnotation() {}

  @Around("fieldAnnotatedWithMyAnnotation()")
  public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println(pjp);
    return "helloworld";
  }
}

When running Application , the console log is going to be:

get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld

The AspectJ manual explains the syntax of field get and set join point signatures and field patterns .


Note: I think that your use case might be a hack rather than a valid application design. You ought to refactor rather than hack into an application like this.

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