使用Byte Buddy拦截器在构造函数中初始化字段

时间:2019-05-02 21:33:45

标签: java byte-buddy

如何在构造函数拦截器中初始化对象字段?

我用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 ,因为事实是,每次创建新的类实例时,都使用相同的HashMap对象实例化 variableNamedField 字段。但是,我只能通过@FieldValue批注将现有字段传递给拦截器。但似乎我无法在拦截器中为该字段分配新变量。

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

1 个答案:

答案 0 :(得分:0)

最简单的方法是使用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));
  }
}