Domanda

I have following structure below (I used annotation @Intercepted to indicate the method which is intercepted): When I call intercepted method as intercepted() without using super keyword an interceptor is called as expected. However when called in the following way super.intercepted() interception is never called. Why is this the case?

public class Base {
   @Intercepted
   public void intercepted() {}
}

public class BaseImpl extends Base {    
   public void doSomething() {
     super.intercepted(); //<-- does not work
     intercepted(); //<--- without the super, it works
   }
}
È stato utile?

Soluzione

The answer lies in the generated byte code, both in the byte code generated by the Java compiler for super.method compared to this.method and in the byte code generated by cglib for the proxy. In order to intercept a method, cglib adds a new class to the type hierarchy. All instances of your proxied class will then be of a different run time type which is a subclass of your proxied class. For your example, this results in a type hierarchy similar to the following:

class Base { 
  void intercepted() { ... }
}

class BaseImpl extends Base { 
  void doSomething() { ... }
}

class BaseImpl$$cglib extends BaseImpl { 
  @Override void intercepted() { ... }
}

Now the Java compiler comes into place. The Java compiler invokes methods by one of for byte code instructions. Each of those instructions result in a different runtime behavior. The two instructions that are important here are the most common:

  • INVOKEVIRTUAL: The invocation this.intercepted is translated into a dynamic/virtual method invocation by the Java compiler. As a consequence of a dynamic call, the Java run time will look at the current type's virtual method table in order to decide which method is to be invoked. The method is bound dynamically. This means, if you are invoking intercepted from an instance of BaseImpl$$cglib, the selected method will be BaseImpl$$cglib.intercepted. If you are invoking the method from a type BaseImpl$$cglib, the method invoked will instead be Base.intercepted because BaseImpl does not override intercepted. Obviously, Base.intercepted will invoke the method defined in its own class.

  • INVOKESTATIC: Static invocations are not bound dynamically at run time. In order to call super.intercepted, the Java run time should invoke the method of the super type's virtual method table. This means, that BaseImpl will explicitly reference the method table of Base. As a result, the virtual method table of BaseImpl$$cglib is never consulted and the calls cannot be intercepted. This is basically the same reason for which it is not possible to intercept static methods. Within byte code, static methods and super invocations are treated exactly the same.

This said, for super.intercepted, the cglib proxy is never hit. It is not possible to instrument loaded classes in Java (well, you can push this rule a little), such that no proxy framework can actually do what you intend.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top