Search code examples
javaspringaopaspectjpointcut

Spring AOP - get old field value before calling the setter


Dear all I am curently using Spring AOP (v4) and AspectJ with load-time-weaver.

I am looking currently for a way to add a dirty flag mechanism into my beans. Therefore I I though of using AOP to call a method before a setter of my beans get called. This I achieved already, but how can I access the old field value beforeit get modified? Or is there a way to get the field name so I can call the getter before the setter get called?

Can anybody provide me here some example how the pointcut/advice has to look like to get it a passed as arguments?

@Aspect
public class MyAspect {

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

Unfortunately it seems that Spring AOP does not support the "set()" field-pointcut construct, is this correct? OR exists someway to use this?

Thank you for any help.


Solution

  • I would recommend to use full AspectJ in combination with a set() pointcut in order to get an efficient solution. But if you do not mind having a slow, ugly solution involving reflection you can also do something like this:

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

    Console log:

    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]