Java 如何使用AOP为JPA实体中的切入点设置者提供建议?

Java 如何使用AOP为JPA实体中的切入点设置者提供建议?,java,jpa,aspectj,Java,Jpa,Aspectj,我需要记录对实体中字段的任何更改—无论是字符串更改,还是对集合/映射的添加/删除 给定一个具有一组基本字段的JPA实体,编写切入点截取字段上的任何集合(..)方法是相当简单的 然而,我陷入困境的是如何编写切入点来处理集合/集合/嵌入等 鉴于以下实体: @Entity public class Provider implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Co

我需要记录对实体中字段的任何更改—无论是字符串更改,还是对集合/映射的添加/删除

给定一个具有一组基本字段的JPA实体,编写切入点截取字段上的任何集合(..)方法是相当简单的

然而,我陷入困境的是如何编写切入点来处理集合/集合/嵌入等

鉴于以下实体:

@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年;
}
}
直接/间接成员更改的日志记录方面:

这方面截获

  • 直接成员更改(
    set()
    pointcut targeting
    Person
    objects)
  • 调用集合+.add()
  • 调用
    Map+.put()
aspect在其
成员
属性中还保留了一个相当复杂的数据结构:一个
映射
,使用集合/映射作为键,使用成对的
个人
字符串
(字段名)元素作为值。为什么数据结构如此复杂?因为同一集合/映射可以分配给多个
Person
成员,甚至可以分配给同一
Person
对象的多个成员,具体取决于您使用的集合/映射的类型。因此,数据结构非常通用。您可以随意扩展driver类,以使用多个
Person
对象和/或在
Person
类中具有多个相同类型的成员。我还没有测试过,但它应该可以工作

更新:

  • 丑陋的
    getOldFieldValue()
    helper方法是必需的,因为AspectJ不需要
    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() );
    }