0

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

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

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

4

2 回答 2

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

于 2014-05-02T19:27:10.777 回答
-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 在大多数情况下表现不佳,但在这种情况下,听起来您要么用错了,要么不知道如何使用它。

于 2017-08-05T04:06:08.510 回答