Spring AOP - 在调用 setter 之前获取旧字段值

Spring AOP - get old field value before calling the setter

亲爱的,我目前正在使用 Spring AOP (v4) 和 AspectJ 以及加载时编织器。

我目前正在寻找一种将脏标志机制添加到我的 bean 中的方法。因此,我虽然在 setter 我的 bean 被调用之前使用 AOP 调用方法。这我已经实现了,但是我怎样才能在它被修改之前访问旧的字段值呢?或者有没有办法获取字段名称,以便我可以在调用 setter 之前调用 getter?

任何人都可以在这里提供一些示例 pointcut/advice 必须如何才能将其作为参数传递?

@Aspect
public class MyAspect {

  @Before("execution(* foo.*.set*(..))") 
  public void beforeSetterCalled(JoinPoint joinPoint){
    System.out.println("beforeSetter");
  }
}

不幸的是 Spring AOP 似乎不支持 "set()" 字段切入点构造,这是正确的吗?或者存在某种方式来使用它?

感谢您的帮助。

你说得对Spring not supporting field joinpoints

Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans). Field interception is not implemented, although support for field interception could be added without breaking the core Spring AOP APIs. If you need to advise field access and update join points, consider a language such as AspectJ.

您将无法使用 Spring AOP 直接通过 AOP 建议获取字段值。

方法与任何字段都不相关。访问器(和修改器)只是 Java 中的约定。如果您遵循该约定,则可以从方法名称中推断字段名称并使用反射来检索它。

我建议将完整的 AspectJ 与 set() 切入点结合使用以获得有效的解决方案。但是如果你不介意有一个涉及反射的缓慢、丑陋的解决方案,你也可以这样做:

package de.scrum_master.app;

public class Person {
    private int id;
    private String firstName;
    private String lastName;

    public Person(int id, String firstName, String lastName) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public int getId() { return id; }
    public String getFirstName() { return firstName; }
    public String getLastName() { return lastName; }

    public void setId(int id) { this.id = id; }
    public void setFirstName(String firstName) { this.firstName = firstName; }
    public void setLastName(String lastName) { this.lastName = lastName; }

    @Override
    public String toString() { return "Person [" + id + ", " + firstName + " " + lastName + "]"; }

    public static void main(String[] args) {
        Person albert = new Person(1, "Albert", "Camus");
        Person audrey = new Person(2, "Audrey", "Hepburn");
        System.out.println(albert);
        System.out.println(audrey);
        System.out.println();
        albert.setId(8);
        albert.setLastName("Einstein");
        audrey.setId(9);
        audrey.setLastName("Tautou");
        System.out.println();
        System.out.println(albert);
        System.out.println(audrey);
    }
}
package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class SetterInterceptor {
    @Before("execution(* set*(*)) && target(instance) && args(newValue)")
    public void beforeSetterCalled(JoinPoint thisJoinPoint, Object instance, Object newValue) {
        String methodName = thisJoinPoint.getSignature().getName();
        try {
            System.out.println(
                methodName.substring(3) + ": " +
                instance
                    .getClass()
                    .getMethod(methodName.replaceFirst("set", "get"))
                    .invoke(instance) +
                " -> " + newValue
            );
        } catch (Exception e) {
            throw new SoftException(e);
        }
    }
}

控制台日志:

Person [1, Albert Camus]
Person [2, Audrey Hepburn]

Id: 1 -> 8
LastName: Camus -> Einstein
Id: 2 -> 9
LastName: Hepburn -> Tautou

Person [8, Albert Einstein]
Person [9, Audrey Tautou]