AspectJを使用すると、アドバイスを使用してメソッドとフィールドを変更できます。
私の例は@AspectJ
、コンパイル時またはロード時にコードを変更する構文で書かれています。@AspectJ
実行時に変更が必要な場合は、この構文もサポートするSpringAOPを使用できます。
単純な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()
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");
}
}
}
最後に、アスペクトで使用される2つのヘルパークラス:
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>
は、最後の側面を回避できたはずです。
これがあなたの質問に答えたことを願っています!