如何使用 Java 自定义注释和 Spring AOP 设置 属性 值?

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

我想使用自定义 Java 注释在使用 Spring AOP (and/or AspectJ) 的私有 class 属性 中插入一个值。快速示例:

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

我希望发生什么?

我将调用并进入 getVarExample(),然后 returns 我希望在控制台或日志中看到“helloworld”。我想以某种方式使用 AOP 将 var1 设置为自定义值。任何将用 @MyAnnotation 注释的 属性 变量将被设置为“helloworld”。我希望上面的例子很清楚。

我尝试了什么?

我确保包名中没有拼写错误,还修改了不同的 AOP 建议注释,例如 @Around@Before。我还在 MyAnnotation 中尝试了不同的目标,结果 ElementType.FIELD 应该是正确的。

你能帮我让它工作吗?

我知道这是可以做到的,但是在网上找不到任何有效的例子。同样,我希望看到 2 个答案:

1.如何让切入点在 MyController 入口触发?我想在 MyAspect class.

enrichVar1(..) 方法中捕获一个断点

2。如何修改MyAspectclass的enrichVar1(..)方法中带注释的var1值?

我不知道我做错了什么。任何帮助将不胜感激。谢谢!

AOP 在我的项目中设置正确。我知道这一点,因为我已经将 AOP 用于不同的事情(例如日志记录)。

更新#1:

请注意,var1 私有变量没有 getter 或 setter。该变量将仅在 MyControllerImpl 内使用。为了更好地说明这一点,我更改了 getVarExample.

的 return 值

AOP 的 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

但对于您的情况,不需要使用字段连接点,您可以将注释放在 getter 上,这应该有效。

正如我在评论中所说:

The pointcut designator @annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set(). I.e., 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.

但是因为你坚持要保持控制器class不变,这里是原生的AspectJ解决方案。请阅读第 Using AspectJ with Spring Applications 章,了解如何使用 @EnableLoadTimeWeaving 和 JVM 参数 -javaagent:/path/to/aspectjweaver.jar.

配置它

为了证明此解决方案确实独立于 Spring 工作,我根本不使用 Spring classes 或注释,仅使用 POJO 和本机 AspectJ。您可以在 Spring 应用程序中简单地执行相同的操作。请注意,与 Spring AOP 方面相比,本机 AspectJ 方面不需要 @Component 注释。

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

当 运行 Application 时,控制台日志将是:

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

AspectJ 手册解释了 field get and set join point signatures and field patterns 的语法。


注意:我认为您的用例可能是 hack 而不是有效的应用程序设计。你应该重构而不是侵入这样的应用程序。