【问题标题】:ByteBuddy: Use new defined field in intercept during construction of classByteBuddy:在类的构造过程中使用新定义的字段拦截
【发布时间】:2017-01-11 08:56:50
【问题描述】:

我正在查看其他人的一些 ByteBuddy 代码。他使用 ByteBuddy 生成运行时子类,这些子类用作代理将他的运行时的一些管理代码实现到特定对象中。

Class<? extends T> newSubClass = new ByteBuddy(ClassFileVersion.ofThisVm())
                .subclass(classType)
                .defineField("_core", Object.class, Visibility.PUBLIC) //<---
                .method(ElementMatchers.isDeclaredBy(classType))
                .intercept(InvocationHandlerAdapter.of((proxy, method, m_args) -> {
                    //TODO: Need to replace core with _core as core is a function argument and will make it bound
                    return proxyHandler(core, method, m_args); //<--
                }))
                .make()
                .load(roleType.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
                .getLoaded();


T proxy = ReflectionHelper.newInstance(newSubClass, args);
newSubClass.getField("_core").set(proxy, core);

为了不将 core 对象直接绑定到 lambda 中,我想使用新定义的字段 _core,这样我就可以重用生成的类(而不是在每次调用函数时重新生成它)。 有没有办法做到这一点?

提前致谢。

【问题讨论】:

    标签: java subclassing byte-buddy


    【解决方案1】:

    您可以像定义方法一样定义自定义构造函数。定义构造函数的一个重要点是您需要另一个构造函数调用作为它的第一条指令。您可以使用MethodCall::invoke 调用构造函数,您可以将其与FieldAccessor::ofField 结合使用。

    这样,您可以定义类似于以下的类:

    new ByteBuddy(ClassFileVersion.ofThisVm())
      .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS)
      .defineConstructor(Visibility.PUBLIC)
        .withParameter(InvocationHandler.class)
        .intercept(MethodCall.invoke(classType.getDeclaredConstructor())
        .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0)))
      .defineField("_core", InvocationHandler.class, Visibility.PUBLIC)
      .method(ElementMatchers.isDeclaredBy(classType))
        .intercept(InvocationHandlerAdapter.toField("_core"))
      .make();
    

    这样,您可以为每个实例设置一个自定义InvocationHandler。如果您只想将状态存储在 _core 字段中并从拦截器访问此字段,请查看 MethodDelegation

    new ByteBuddy(ClassFileVersion.ofThisVm())
      .subclass(classType, ConstructorStrategy.Default.NO_CONSTRUCTORS)
      .defineConstructor(Visibility.PUBLIC)
        .withParameter(Object.class)
        .intercept(MethodCall.invoke(classType.getDeclaredConstructor())
        .andThen(FieldAccessor.ofField("_core").setsArgumentAt(0)))
      .defineField("_core", Object.class, Visibility.PUBLIC)
      .method(ElementMatchers.isDeclaredBy(classType))
        .intercept(MethodDelegation.to(MyHandler.class))
      .make();
    
    public class MyHandler {
      @RuntimeType
      public static Object intercept(@FieldValue("_core") Object value) { ... }
    }
    

    您可能需要的其他注释是@This@AllArguments@Origin@SuperCall。您需要的越少,代理的效率就越高。尤其是@AllArguments,由于其分配要求,价格昂贵。

    请注意,在这种情况下,您的字段仅在超级构造函数调用之后设置。此外,您假设超类型中有一个默认构造函数。或者,您可以实现自定义ConstructorStrategy

    至于缓存,看看Byte Buddy的TypeCache

    【讨论】:

    • 感谢 w.r.t 的提示。缓存。否则我会使用WeakHashMap&lt;Class&lt;?&gt;, Class&lt;?&gt;&gt;
    • 这不起作用,因为该值是键的子类并强烈引用它。而是使用 TypeCache 来解决这个问题,它也可以轻柔地或弱地引用该值。
    • 我仍然不清楚如何访问.intercept(InvocationHandlerAdapter.of((proxy, method, m_args) -&gt; proxyHandler(core, method, m_args); 中的新_core 参数
    • 您不想委托给存储在_core 中的调用处理程序吗?在这种情况下,请查看 MethodDelegation 实现。在此类中,您可以通过 @FieldValue("_core") 注释访问属性。请参阅我的更新答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    相关资源
    最近更新 更多