首页 > 解决方案 > 如何装饰现有 Java 对象的方法?

问题描述

编辑:我在https://stackoverflow.com/a/60235242/3236516描述了我们的解决方案

我有一个 java 对象。它是扩展抽象类的许多子类之一的实例。我想修改它的一种方法,以便在调用原始方法之前运行一些额外的代码。我的目标在概念上与 AspectJ 中的切入点相同。

如果我创建原始对象的一些修改版本而不是改变原始对象,那很好。如果解决方案涉及字节码操作,也可以。

前期工作

我考虑过通过 JavaAssist 创建代理。麻烦的是,ProxyFactory 的 create 方法要求我提前知道构造函数的输入类型。我不。我可以在不通过 Objenesis 调用构造函数的情况下创建我的对象,但是生成的代理对象对于构造函数设置的任何值都将具有空值。这意味着只要直接引用构造函数设置的值,我生成的对象的行为就会与原始对象不同。

语境

我们通过 AWS Kinesis Data Analytics 使用 Flink 来转换一些流数据。我们希望在所有StreamOperator 的open() 方法的开头包含一些通用代码,而无需修改每个运算符。一个用例是确保自定义指标代理在操作员运行的每个实例上运行。

标签: javaapache-flinkbyte-buddybytecode-manipulationjava-assist

解决方案


使用 Byte Buddy,您可以创建一个包装器或一个 Java 代理,它们都可以实现此目标。如果您在调用包装类的构造函数时遇到困难,那么使用 Byte Buddy 也会出现同样的问题,因为任何库都绑定到 JVM 给出的约束。

要创建 Java 代理,请使用AgentBuilder. 然后,您可以使用该type步骤指定要拦截的所有类型,例如实现某个接口或扩展类的所有类型。对于transform,Byte Buddy 提供了一个方法装饰 API 称为Advice,它允许您添加额外的代码,例如:

class MyAdvice {
  @Advice.OnMethodEnter
  static void enter() { System.out.println("Hello"); }
}

经过

builder = builder.visit(Advice.to(MyAdvice.class).on(named("foo")));

例如,您可以在您指定的类型的所有名为“foo”的方法的开头打印 hello world。您可以在包的包文档中java.instrument找到有关 Java 代理的更多信息。


推荐阅读