【问题标题】:Is there a runtime proxy creation library that supports to retain annotations of the proxied class? [closed]是否有运行时代理创建库支持保留代理类的注释? [关闭]
【发布时间】:2014-06-19 13:49:54
【问题描述】:

当使用例如cglibjavassist proxies 创建代理时,该代理是通过创建代理目标的子类来实现的。但是,这意味着此代理上的注释将丢失。当一个类由两个库处理时,这是有问题的:

  1. 第一个库需要创建给定类的代理才能运行。
  2. 第二个库通过读取对象的注释来处理对象。

对于第二个库,同时使用第一个库时,注释已消失。问题是:是否存在具有高级 API 的运行时代码生成库,可以轻松保留代理类的注释?

【问题讨论】:

    标签: java code-generation bytecode proxy-classes byte-buddy


    【解决方案1】:

    Byte Buddy 是一个用于运行时生成 Java 类的库。它的功能不仅限于创建代理类,而且代理类的创建是一个明显的用例。

    假设,我们正在处理以下代码:

    @Retention(RetentionPolicy.RUNTIME)
    @interface MyAnnotation { }
    
    @MyAnnotation
    class Foo {
      @MyAnnotation
      public void bar() { }
    }
    

    然后我们可以在运行时创建一个覆盖bar 方法的子类。 bar 方法的重写实现被实现为简单地调用其超级实现:

    Class<?> runtimeType = new ByteBuddy()
      .withAttribute(TypeAttributeAppender.ForSuperType.INSTANCE)
      .withDefaultMethodAttributeAppender(MethodAttributeAppender.ForInstrumentedMethod.INSTANCE)
      .subclass(Foo.class)
      .method(named("bar")).intercept(SuperMethodCall.INSTANCE)
      .make()
      .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
      .getLoaded();
    

    使用上面的运行时类,我们现在可以验证结果类型:

    assertNotEquals(Foo.class, runtimeType);
    assertThat(runtimeType.isAnnotationPresent(MyAnnotation.class)), is(true));
    assertThat(runtimeType.getDeclaredMethod("bar").isAnnotationPresent(MyAnnotation.class)), is(true));
    

    尽管有子类,但类型和方法都由MyAnnotation 注释。通过调用getDeclaredMethod,我们进一步验证子类实际上定义了一个新方法。

    披露:我是 Byte Buddy 的作者,我想为这个问题提供一个答案,这个问题在稍微更具体的上下文中经常在 SO 上被问到。此外,我想借此机会为 Byte Buddy 创建一个 SO 标签。

    【讨论】:

      【解决方案2】:

      我的意思是...或者您可以只使用 Javassist 并重置方法的主体、附加方法的结尾或附加到方法的开头。或者只是在类中添加一个新方法,并没有真正理解这一点。

      假设您的班级有 yourMethodName 方法。我觉得 Foo Bar 并没有帮助人们并导致更多的混乱。因此,我将尝试使用所选方法名称提供更易于理解的内容。这组 Javassist 函数实际上可以在代码中工作。

      ClassPool classPool = HookManager.getInstance().getClassPool();
      CtClass ctClass = classPool.getCtClass("com.domain.YourClassName");
      ctClass.getMethod("yourMethodName", "()V").insertAfter("FunCreator funCreator = new FunCreator.funMethod()");
      

      insertAfter 放在方法的末尾,insertBefore 放在方法的开头,如果你想重写整个方法而不改变它的调用方式,你也可以使用 setBody。

      请注意,这仅适用于启动时的类加载,如果您想在运行时在代理的帮助下更改方法,您可能希望挂接到对该方法的调用,然后发出它。 Byte Buddy 有代理,Javassist 没有。 Javassist 在大多数情况下表现不佳,但在这种情况下,听起来您要么用错了,要么不知道如何使用它。

      【讨论】:

        猜你喜欢
        • 2010-12-14
        • 2011-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-22
        • 2014-11-12
        • 2019-03-27
        • 1970-01-01
        相关资源
        最近更新 更多