CGLib Enhancer with anonymous class

2019-06-08 11:29发布

I'm using CGLib to proxy classes. I've hit a problem where I can not enhance anonymous classes because they don't have a default constructor.

My code looks like this:

Enhancer enhancer = new Enhancer();
enhancer.setCallback(new MethodInterceptor() { .... });
enhancer.setSuperclass(type); // type is the Class I'm trying to enhance
enhancer.create();

However this throws the following exception if the enhanced class is anonymous (no default constructor).

java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given

Is there a way to go around this?

1条回答
时光不老,我们不散
2楼-- · 2019-06-08 12:18

A JVM's verifier assures that you call a valid constructor chain. The only way around this is to disable the verifier by command line when starting up the JVM and this is nothing you generally want to do as it introduces several insecurities to your application.

Thus, you will have to call a valid constructor of your anonymous class which is imitated by the subclass that is created by cglib. This class's constructor thus takes an instance of its enclosing class as its argument. If you use cglib to create a pure proxy that never calls a real method, you can consider handing null to this constructor but you still need to select the right constructor before handing the argument.

You can achieve this by calling:

enhancer.create(new Class<?>[] {type.getEnclosingClass()}, new Object[] {null})

where the above call selects the correct constructor and initializes the anonymous class's outer instance field with a null value.

Alternatively, you could use the internal OpenJDK class ReflectionFactory in order to create an instance without calling any constructor. This is possible after calling enhancer.createClass(). This requires however additional preparation as you need to specify a different callback.

However, you might just want to drop cglib alltogether. As a disclaimer, I am the author of an alternative library I want to recommend to you. Using Byte Buddy, you have more freedom in creating classes and it is easier to define your logic using it. Using Byte Buddy, you can write

new ByteBuddy()
  .subclass(type)
  .method(any()).intercept(MethodDelegation.to(new Object() {
     @RuntimeType
     public Object intercept(@SuperCall Callable<?> c, 
                             @Origin Method m, 
                             @AllArguments Object[] a) throws Exception {
       // implement your interception logic
     }
   }).make();

and achieve the same logic. Simply remove the (annotated) arguments you do not need for calling your interception.

查看更多
登录 后发表回答