使用 AspectJ,您可以使用建议修改方法和字段。
我的示例是用@AspectJ
在编译时或加载时修改代码的语法编写的。如果你想在运行时进行修改,你可以使用同样支持这种@AspectJ
语法的 Spring AOP。
一个简单的 Person 类和一个存根存储库的示例。所有关于哪些字段被更新的信息都由一个称为 SetterAspect 的方面处理。它监视在写入字段时更新了哪些字段。
此示例中的另一个建议是围绕存储库中的更新方法。这是检索从第一个方面收集的数据。
人员类:
public class Person {
private String firstName;
private String lastName;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public static void main(String[] args) {
Person person = new Person();
person.setFirstName("James");
person.lastName = "Jameson";
DtoRepository<Person> personRepository = new DtoRepository<Person>();
personRepository.update(person);
}
}
存根存储库:
public class DtoRepository<T> {
public void update(T t) {
System.out.println(t.getClass().getSimpleName() + " updated..");
}
public void updatePerson(T t, Set<String> updatedFields) {
System.out.print("Updated the following fields on " +
t.getClass().getSimpleName() + " in the repository: "
+ updatedFields);
}
}
main()
使用 AspectJ 在 Person 类中执行方法的输出:
更新了存储库中 Person 的以下字段:[lastName, firstName]
这里需要注意的重要一点是 main() 方法调用了DtoRepository.update(T t)
但是DtoRepository.update(T t, Set<String> updatedFields)
由于存储库方面的环绕通知而被执行。
监视对演示包中私有字段的所有写入的方面:
@Aspect
public class SetterAspect {
private UpdatableDtoManager updatableDtoManager =
UpdatableDtoManager.INSTANCE;
@Pointcut("set(private * demo.*.*)")
public void setterMethod() {}
@AfterReturning("setterMethod()")
public void afterSetMethod(JoinPoint joinPoint) {
String fieldName = joinPoint.getSignature().getName();
updatableDtoManager.updateObjectWithUpdatedField(
fieldName, joinPoint.getTarget());
}
}
存储库方面:
@Aspect
public class UpdatableDtoRepositoryAspect {
private UpdatableDtoManager updatableDtoManager =
UpdatableDtoManager.INSTANCE;
@Pointcut("execution(void demo.DtoRepository.update(*)) " +
"&& args(object)")
public void updateMethodInRepository(Object object) {}
@Around("updateMethodInRepository(object)")
public void aroundUpdateMethodInRepository(
ProceedingJoinPoint joinPoint, Object object) {
Set<String> updatedFields =
updatableDtoManager.getUpdatedFieldsForObject(object);
if (updatedFields.size() > 0) {
((DtoRepository<Object>)joinPoint.getTarget()).
updatePerson(object, updatedFields);
} else {
// Returns without calling the repository.
System.out.println("Nothing to update");
}
}
}
最后,方面使用的两个辅助类:
public enum UpdatableDtoManager {
INSTANCE;
private Map<Object, UpdatedObject> updatedObjects =
new HashMap<Object, UpdatedObject>();
public void updateObjectWithUpdatedField(
String fieldName, Object object) {
if (!updatedObjects.containsKey(object)) {
updatedObjects.put(object, new UpdatedObject());
}
UpdatedObject updatedObject = updatedObjects.get(object);
if (!updatedObject.containsField(fieldName)) {
updatedObject.add(fieldName);
}
}
public Set<String> getUpdatedFieldsForObject(Object object) {
UpdatedObject updatedObject = updatedObjects.get(object);
final Set<String> updatedFields;
if (updatedObject != null) {
updatedFields = updatedObject.getUpdatedFields();
} else {
updatedFields = Collections.emptySet();
}
return updatedFields;
}
}
和
public class UpdatedObject {
private Map<String, Object> updatedFields =
new HashMap<String, Object>();
public boolean containsField(String fieldName) {
return updatedFields.containsKey(fieldName);
}
public void add(String fieldName) {
updatedFields.put(fieldName, fieldName);
}
public Set<String> getUpdatedFields() {
return Collections.unmodifiableSet(
updatedFields.keySet());
}
}
我的示例使用方面执行所有更新逻辑。如果所有 DTO 都实现了一个返回 a 的接口Set<String>
,那么您可以避免最后一个方面。
我希望这回答了你的问题!