如何使用代码生成跟踪 POJO 的状态

How to track the state of a POJO using code generation

我们正在寻找一种解决方案,以高效的方式跟踪客户端 POJO 实例的状态。我们期望的是:每次对 POJO 进行更改时,都会使用 setters 来创建此状态。我们创建了一个基于 OGNL 的监视/事件总线,当进行更改时,我们将向我们的事件总线发送适当的 OgnlChangeEvent

到目前为止,我们研究了 AspectJ / cglib / 对象图 Diff 解决方案,但它们都占用了太多 CPU 时间。我们当前的解决方案基于 Spring MethodInterceptor 并且每次调用 Getter 方法时我们都会创建一个新的 Proxy 实例。

此时我们开始研究代码生成解决方案,我们偶然发现了 Byte Buddy。这个方向是正确的方法吗?我们可以生成一个新的 Class 来扩展我们的客户端 POJO 状态并在调用 setter 方法之前通知其 OGNL 前缀吗?

Byte Buddy是一个代码生成工具,当然可以实现这样的方案。要创建拦截 setter 的 class,您可以编写如下代码:

new ByteBuddy()
  .subclass(UserPojo.class)
  .method(ElementMatchers.isSetter())
  .intercept(MethodDelegation.to(MyInterceptor.class)
             .andThen(SuperMethodCall.INSTANCE)
  .make();

你会在哪里编写这样的拦截器:

public class MyInterceptor {
  public static void intercept(Object value) {
    // business logic comes here
  }
}

这样,您可以在每次调用 setter 时添加一些代码,该 setter 在原始代码之前触发。您还可以使用所有基本类型重载拦截方法,以避免对参数进行装箱。字节好友想办法为你做。

但是我很困惑你所说的性能是什么意思。上面的代码对我来说与创建 class 相同:

class UserClass {
  String value;
  void setValue(String value) {
    this.value = value;
  }
}

class InstrumentedUserClass extends UserClass {
  @Override
  void setValue(String value) {
    MyInterceptor.intercept(value);
    super.setValue(value);
  }
}

性能主要受您在 intercept 方法中执行的操作的性能影响。

最后,我不明白 cglib 为什么对你不起作用,但使用 Spring - 它是在 cglib 之上构建的 - 确实有效。我怀疑你的拦截逻辑有问题,你应该调查一下。

我认为性能在很大程度上取决于您使用的字节码检测框架,而取决于您在方法拦截器中执行的操作。最后只有自己测量了才知道。

我对你的用例了解不多,但总的来说我会问自己:

  • 我真正需要什么信息?
  • 获取该信息的基本数据是什么?

您应该将基本数据收集与数据(信息)的解释分开。通常解释需要更多的时间。基础数据是不能从其他数据中推导出来的数据。例如生日是基本数据,而年龄是从生日派生的。

在方法拦截器中我会

  • 只收集class名称、方法名称和可能的参数等基本数据。
  • 将此信息发送到某种工作队列
  • 让后台工作人员生成信息、记录或保存它。

后台工作者可以,例如解释方法名称以查明它是否是 属性 访问器。通常,您使用 BeanInfo that you get from an Introspector 或至少反射 api 来执行此操作。