【问题标题】:Initiating fields in constructors with Byte Buddy interceptors使用 Byte Buddy 拦截器在构造函数中初始化字段
【发布时间】:2019-05-02 21:33:45
【问题描述】:

如何在构造函数拦截器中启动对象字段?

我使用 Byte Buddy 创建了一个构造函数,如下面的代码所示。

Class<?> klass = new ByteBuddy()
            .subclass(Object.class, ConstructorStrategy.Default.NO_CONSTRUCTORS)

            .defineProperty("origin", CoreObject.class, true)
            .defineProperty("variableNamedField", Map.class, true)

            .defineConstructor(Visibility.PUBLIC)
            .withParameters(CoreObject.class)
            .intercept(
                    // Invoke Objects default constructor explicitly
                    MethodCall.invoke(Object.class.getConstructor())
                            .andThen(FieldAccessor.ofField("origin").setsArgumentAt(0))
                            .andThen(FieldAccessor.ofField("variableNamedField").setsValue(new HashMap<>()))
                            .andThen(MethodDelegation.to(new FillMapInterceptor("variableNamedField")))

                    //
                    // I have to fill the map.
                    // Something like this:
                    //
                    // variableNamedField.put("first", new FirstHandler(origin));
                    // variableNamedField.put("second", new SecondHandler(origin));
                    //

            )
            .make()
            .load(CoreObject.class.getClassLoader())
            .getLoaded();

首先构造函数将参数保存到私有字段。然后它创建集合。然后它调用以下拦截器来填充该集合。

class FillMapInterceptor {

    private final String mapField;

    public FillMapInterceptor(String mapField) {
        this.mapField = mapField;
    }

    public void construct(@FieldValue("variableNamedField") Map<String, Handler> map, @FieldValue("origin") CoreObject coreObject){
        map.put("first", new FirstHandler(coreObject));
        map.put("second", new SecondHandler(coreObject));
    }
}

最好在拦截器中实例化 variableNamedField 字段,因为事实证明,每次创建新的类实例时,variableNamedField 字段都会用相同的 HashMap 对象实例化。但是,我只能通过 @FieldValue 注释将现有字段传递给拦截器。但似乎我无法在拦截器中分配带有新变量的字段。

  1. 如何在构造函数拦截器中启动类字段?我只能在拦截器中使用@FieldValue 来处理现有字段值。
  2. 如何使字段名称任意? @FieldValue 之类的注解需要常量参数(例如字段名称)。也许,我可以以某种方式从 defineProperty 方法结果中获取字段并将其传递给拦截器或用作拦截器的字段?
  3. 如何将构造函数参数直接传递给拦截器?我不喜欢先初始化字段,然后通过@FieldValue注解将字段传递给拦截器的方式。

【问题讨论】:

    标签: java byte-buddy


    【解决方案1】:

    最简单的方法是使用Advice 类定义构造函数。通知类允许您使用稍后内联的模板定义代码。在这种情况下,您可以使用@Advice.OnMethodExit 在上面的Implementation 之后添加代码,然后将您在上面已经创建的代码环绕起来:

    Advice.to(YourAdvice.class).wrap(...)
    

    您基本上可以在 javadoc 中找到有关定义建议的所有信息,您可以将上面的代码复制粘贴到静态方法中以内联它:

    class YourAdvice {
      @Advice.OnMethodExit
      static void exit(@Advice.FieldValue("variableNamedField") 
            Map<Object, Object> variableNamedField) {
        variableNamedField.put("first", new FirstHandler(origin));
        variableNamedField.put("second", new SecondHandler(origin));
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-28
      • 1970-01-01
      • 2012-04-11
      • 1970-01-01
      相关资源
      最近更新 更多