Java 如何使用AOP为JPA实体中的切入点设置者提供建议?
我需要记录对实体中字段的任何更改—无论是字符串更改,还是对集合/映射的添加/删除 给定一个具有一组基本字段的JPA实体,编写切入点截取字段上的任何集合(..)方法是相当简单的 然而,我陷入困境的是如何编写切入点来处理集合/集合/嵌入等 鉴于以下实体:Java 如何使用AOP为JPA实体中的切入点设置者提供建议?,java,jpa,aspectj,Java,Jpa,Aspectj,我需要记录对实体中字段的任何更改—无论是字符串更改,还是对集合/映射的添加/删除 给定一个具有一组基本字段的JPA实体,编写切入点截取字段上的任何集合(..)方法是相当简单的 然而,我陷入困境的是如何编写切入点来处理集合/集合/嵌入等 鉴于以下实体: @Entity public class Provider implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Co
@Entity
public class Provider implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
private Long id;
private String name;
@Column(name="type", nullable=false)
@Enumerated(EnumType.STRING)
private ProviderType providerType;
@ManyToMany
private List<Contact> contacts;
@Embedded
private Validity validity;
// setters and getters omitted for brevity
}
我可以给它写一个事前/事后/事后建议
before( Object val, Object o) : fieldSetter{
String fieldName = thisJoinPointStaticPart.getSignature().getName();
System.out.println( "Object being changed: " + o.toString() );
System.out.println( "New Value for: " + fieldname + " is: " + v.toString() );
}
但是如何处理嵌入对象或集合的这种情况呢?对于一个嵌入式对象,如果我只是在对象中的setter方法周围放置我的建议,那么我如何知道哪个是实际被修改/持久化的父对象
对于集合/集合/映射等,我如何建议不要使用添加/删除方法?最后我需要做的是建议getCollection().add()方法以及getCollection.remove()方法。但我似乎找不到一个好办法。这不能直接完成,只能通过手动簿记,因为集合或映射在调用方法时不会更改其标识,只更改其内部状态,即没有要拦截的
set()
joinpoint,只有方法调用。因此,您需要维护分配给您感兴趣的对象成员的集合/映射之间的映射,并跟踪它们的更改,这相当繁琐。下面是一些示例代码,其中包含用于Collection.add()
和Map.put()
的概念验证。您必须将其扩展到所有更改内部状态的方法,例如remove()
,clear()
等。基本上,它是这样工作的:
驱动程序类别:
这是一个示例Person
类,包含两个基本成员、两个集合和一个映射。它
- 为所有
成员指定默认值Person
- 改变他们
- 取消分配现有集合/映射
成员,将其保存在局部变量中Person
- 再次更改集合/映射对象(由于对象当前未分配给
成员,因此不应产生任何日志输出)Person
- 将集合/映射对象重新分配给
成员Person
- 再次更改它们(现在将再次生成日志输出)
package de.scrum\u master.app;
导入java.util.ArrayList;
导入java.util.HashMap;
导入java.util.HashSet;
导入java.util.List;
导入java.util.Map;
导入java.util.Set;
公共阶层人士{
int-id;
字符串名;
列表对象=新的ArrayList();
Set number=新的HashSet();
映射属性=新的HashMap();
公众人物(整数id,字符串名称){
超级();
this.id=id;
this.name=名称;
}
@凌驾
公共字符串toString(){
返回“Person[id=“+id+”,name=“+name+””;
}
公共静态void main(字符串[]args){
System.out.println(“创建个人对象”);
个人=新人(2,“沃纳·海森堡”);
System.out.println(“\n更改成员对象状态”);
person.id=1;
person.name=“阿尔伯特·爱因斯坦”;
person.objects.add(“foo”);
增加(11);
person.objects.add(新对象());
增加(11);
增加(22);
增加(33)人;
人.财产.put(“出生年份”,1879年);
人.财产.put(“死亡年份”,1955年);
person.properties.put(“众所周知”,新字符串[]{“光电效应”,“狭义相对论”,“广义相对论”});
System.out.println(“\n未分配成员对象”);
列出对象=person.objects;
person.objects=null;
设置号码=个人号码;
person.number=null;
映射属性=person.properties;
person.properties=null;
System.out.println(“\n更改非成员对象状态”);
对象。添加(“条”);
增加(22);
添加(新对象());
增加(44);
增加(55);
增加(66);
“诺贝尔奖年”,1921年;
System.out.println(“\n重新分配成员对象”);
person.objects=对象;
person.number=数字;
person.properties=属性;
System.out.println(“\n再次更改成员对象状态”);
person.objects.add(“zot”);
增加(33);
person.objects.add(新对象());
增加(77);
增加(88人);
人.数.加(99);;
“世纪风云人物”,1999年;
}
}
直接/间接成员更改的日志记录方面:
这方面截获
- 直接成员更改(
pointcut targetingset()
objects)Person
- 调用集合+.add()
- 调用
Map+.put()
成员
属性中还保留了一个相当复杂的数据结构:一个映射
,使用集合/映射作为键,使用成对的个人
和字符串
(字段名)元素作为值。为什么数据结构如此复杂?因为同一集合/映射可以分配给多个Person
成员,甚至可以分配给同一Person
对象的多个成员,具体取决于您使用的集合/映射的类型。因此,数据结构非常通用。您可以随意扩展driver类,以使用多个Person
对象和/或在Person
类中具有多个相同类型的成员。我还没有测试过,但它应该可以工作
更新:
- 丑陋的
helper方法是必需的,因为AspectJ不需要getOldFieldValue()
before( Object val, Object o) : fieldSetter{ String fieldName = thisJoinPointStaticPart.getSignature().getName(); System.out.println( "Object being changed: " + o.toString() ); System.out.println( "New Value for: " + fieldname + " is: " + v.toString() ); }