Spring AOP - get old field value before calling th

2019-03-22 05:49发布

问题:

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.

回答1:

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]


回答2:

You are correct about 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.

You won't be able to use Spring AOP to get the field value through the AOP advice directly.

A method is not related to any field. Accessors (and mutators) are just a convention in Java. If you are following that convention, you can infer the field name from the method name and use reflection to retrieve it.