【问题标题】:Add field to Proxy class created with Javassist将字段添加到使用 Javassist 创建的代理类
【发布时间】:2013-12-17 17:31:20
【问题描述】:

我正在使用 Javassist ProxyFactory 创建一个代理类,代码如下:

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
.....
Class clazz = factory.createClass();
Object result = clazz.newInstance();

问题是我还需要在类中添加一个字段。但是如果我这样做CtClass proxy = ClassPool.getDefault().get(clazz.getName()); 它会给出NotFoundException

如何添加使用 createClass 创建的类的字段?有没有更好的方法来做我想做的事情?

【问题讨论】:

  • 它似乎不是为了操纵这些代理类。不会有任何代码使用该字段。如果您想要一个重要的类,请使用类工厂。
  • 该字段是否会被您的 MyCustomInterface 中公开的方法使用?
  • 没错,该字段将被 MyCustomInterface 中的方法使用。 AFAIK java 不允许在接口中声明实例字段。

标签: java javassist bytecode-manipulation


【解决方案1】:

这是基于您对我的评论的回复。

您确实可以使用MyCustomInterface 和您的proxyClass 在Java 中创建一种mixin。但是您仍然必须从代理类转换为 MyCustomInterface 才能调用这些方法。

让我们开始吧。

创建您的代理

首先你创建你的代理你已经在做什么了:

 // this is the code you've already posted
 ProxyFactory factory = new ProxyFactory();
 factory.setSuperclass(entity.getClass());
 factory.setInterfaces(new Class[] { MyCustomInterface.class });

方法处理程序:发挥作用

Javassist 代理允许您添加MethodHandler。它的作用基本上是在常规 Java 代理中具有 InvocationHandler,这意味着它可以作为方法拦截器。

方法处理程序将是您的混音!首先,您创建一个新的 MethodHandler,其中包含您实际要添加到类中的自定义字段,以及您已开始代理的实体对象:

  public class CustomMethodHandler implements MethodHandler {

    private MyEntity objectBeingProxied;
    private MyFieldType myCustomField;

    public CustomMethodHandler(MyEntity entity) {
       this.objectBeingProxied = entity;
    }

    // code here with the implementation of MyCustomInterface
    // handling the entity and your customField

    public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable {
          String methodName = method.getName();

          if(methodNameFromMyCustomInterface(methodName)) {
            // handle methodCall internally: 
            // you can either do it by reflection
            // or if needed if/then/else to dispatch
            // to the correct method (*) 
          }else {
             // it's just a method from entity let them
             // go. Notice we're using proceed not method!

             proceed.invoke(objectBeingProxied,args);
          }
    }
  }

(*) 请注意,即使我在注释中说要在内部处理调用,您也可以将接口实现放在不是您的方法处理程序的另一个地方,然后从这里调用它。

把所有东西放在一起

ProxyFactory factory = new ProxyFactory();
factory.setSuperclass(entity.getClass());
factory.setInterfaces(new Class[] { MyCustomInterface.class });
Class cls = factory.createClass();

// bind your newly methodHandler to your proxy
((javassist.util.proxy.Proxy) cls).setHandler(new CustomMethodHandler(entity));
EntityClass proxyEntity = cls.newInstance();

您现在应该可以使用((MyCustomInterface)proxyEntity).someMethodFromTheInterface() 并让它由您的CustomMethodHandler 处理

总结

  • 您使用 javassist 中的代理工厂创建代理
  • 您创建自己的 MethodHandler 类,该类可以接收您的代理实体和您要操作的字段
  • 您将 methodHandler 绑定到您的代理,以便您可以委托接口实现

请记住,这些方法并不完美,这是 Entity 类中的代码无法引用接口的缺点之一,除非您首先创建代理。

如果您有什么不清楚的地方,请发表评论,我会尽力为您澄清。

【讨论】:

  • 非常感谢!这是个好主意……顺便提一下,MethodHandler 是一个接口,而不是一个类,所以我必须实现 MethodHandler(而不是扩展)
  • 嗨费尔南多,很高兴我能帮上忙!抱歉 MethodHandler 不匹配,我是在脑海中写出来的,那里没有编译器帮助;-) 我将编辑帖子以更正该细节。
  • 如果你能凭脑子写出所有这些东西,那你就是个天才(希望不是疯子)
  • @Fernando:我不是天才,即使有人说我疯了,但我通常不同意,但我喜欢将自己视为一个有创造力的人 ;-) 在我的工作中,我已经创建了一些工具依赖于 Javassist,我也做了很多 JVM 巫术,所以你问的这种事情是我日常工作的一部分......这就是为什么它不在我的脑海中。
  • @DiegoMacario:您好 Diego,也许您应该尝试发布问题以获得更好的帮助。据我了解,您有一个实现 MethodHandler 的对象并且有一个您想要访问的字段,是吗?通过反射你应该能够得到它,记住你不想通过 MethodHandler 而是通过对象的类(做一个 getClass())并记住通过反射你可能需要担心 getFields 和 getDeclaredFields 和层次结构(如果存在)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-19
  • 1970-01-01
相关资源
最近更新 更多