Question

When creating a proxy with for example cglib or javassist proxies, this proxy is implemented by creating a subclass of the proxy target. However, this means that the annotations on this proxy are lost. This is problematic when a class is processed by two libraries where:

  1. The first libraries requires the creation of a proxy of a given class to function.
  2. The second library process objects by reading annotations from them.

For the second library, the annotations have disappeared when simultaneously using the first library. The question is: Does there exist a runtime code generation library with a high-level API that allows for an easy retention of the annotations of the proxied class?

Was it helpful?

Solution

Byte Buddy is a library for the runtime generation of Java classes. Its capabilities are not limited to the creation of proxy classes but the creation of proxy classes is an obvious use case.

Assuming, we are dealing with the following code:

@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation { }

@MyAnnotation
class Foo {
  @MyAnnotation
  public void bar() { }
}

Then we can create a subclass at runtime that overrides the bar method. The overridden implementation of the bar method is implemented to simply call its super implementation:

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();

With the above runtime class, we can now verify the resulting type:

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

Both the type and the method are annotated by MyAnnotation despite the subclass. By calling getDeclaredMethod, we furthermore verify that the subclass actually defines a new method.

Disclosure: I am the author of Byte Buddy and I wanted to provide an answer to this question that is often asked on SO in a slightly more specific context. Furthermore, I wanted to take the opportunity to create a SO tag for Byte Buddy.

OTHER TIPS

I mean...or you could just take Javassist and either reset the body of the method, append the end of the method, or append to the beginning of the method. Or just add a new method to the class, not really understanding the point in this.

Lets say your class had the yourMethodName method. I feel like Foo Bar does not help people and leads to more confusion. So I will try to attempt to give something more understandable with the chosen method names. This set of Javassist functions can actually work in a code.

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

insertAfter goes at the end of the method, insertBefore goes at the beginning of the method, and you also have setBody if you want to rewrite the entire method without changing how it may be called.

Note this works on class loading at startup only, if you wanted to change the method during runtime with the assistance of an agent you would probably wish to hook into a call to the method and then issue it. Byte Buddy has an agent, Javassist doesnt. Javassist doesn't perform as well in most cases, but in this case it sounds like you either used it wrong or didn't know how to use it no offense.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top