С AspectJ вы можете изменять методы и поля с советами.
Мой пример написан с синтаксисом @AspectJ
, который изменяет код во время компиляции или загрузки. Если вы хотите внести изменения во время выполнения, вы можете использовать Spring AOP, который также поддерживает этот синтаксис @AspectJ
.
Пример с простым классом Person и хранилищем-заглушкой. Вся информация о том, какие поля обновляются, обрабатывается аспектом, называемым SetterAspect. Он отслеживает, какие поля обновляются при записи в них.
Другой совет в этом примере - метод обновления в хранилище. Это для извлечения данных, собранных из первого аспекта.
Класс Person:
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()
в классе Person с AspectJ:
Обновлены следующие поля в Персоне
в репозитории: [lastName,
ПгвЬЫате]
Здесь важно отметить, что метод 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 реализовали интерфейс, который возвращает Set<String>
, вы могли бы избежать последнего аспекта.
Надеюсь, это ответило на ваш вопрос!