是否有运行时代理创建库,它支持保留代理类的注释?

时间:2014-05-02 19:27:11

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

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

  1. 第一个库需要创建一个给定类的代理才能运行。
  2. 第二个库通过从中读取注释来处理对象。
  3. 对于第二个库,当同时使用第一个库时,注释已消失。问题是:是否存在具有高级API的运行时代码生成库,可以轻松保留代理类的注释?

2 个答案:

答案 0 :(得分:4)

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标签。

答案 1 :(得分:-1)

我的意思是......或者您可以使用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在大多数情况下表现不佳,但在这种情况下,听起来你使用它错了或者不知道如何使用它而不是冒犯。