Callback是Cglib中一个比较核心的概念,看一下下面的示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Intercept!");
methodProxy.invokeSuper(o, objects);
return null;
}
});
Person person = (Person) enhancer.create();
person.sayHello();
}
}
其中,先给Enchancer
类设置一个需要被代理的类Person
,然后设置Callback
。Callback
可以理解成生成的代理类的方法被调用时(比如说调用person.sayHello()
时),会执行的逻辑。
Callback
是一个空的接口,在Cglib中它的实现类有以下几种:
MethodInterceptor
NoOp
LazyLoader
Dispatcher
InvocationHandler
FixedValue
下面分别来介绍一下。首先是MethodInterceptor
,这是一个功能很强大的接口,它可以实现类似于AOP编程中的环绕增强(around-advice)。它只有一个方法public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
。代理类的所有方法调用都会转而执行这个接口中的
MethodProxy proxy) throws Throwable;intercept
方法而不是原方法。如果需要在intercept
方法中执行原方法可以使用参数method
进行反射调用或者使用参数proxy
,后者会快一些(反射调用比正常的方法调用的速度慢很多)。MethodInterceptor
允许我们完全控制被拦截的方法,并且提供了手段对原方法进行调用,那为什么还会有其它的Callback
接口实现呢?因为 MethodInterceptor
的效率不高,它需要产生不同类型的字节码,并且需要生成一些运行时对象(InvocationHandler
就不需要),所以Cglib提供了其它的接口供我们选择。
接下来是NoOp
,就像它的名字一样,这个接口只是简单地把方法调用委托给了被代理类的原方法(本例中是Person
),不做任何其它的操作。然后看一下LazyLoader
,它也提供了一个方法:Object loadObject() throws Exception;
,loadObject()
方法会在第一次被代理类的方法调用时触发,它返回一个代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用,就像它的名字说的那样,一种lazy
模式。如果被代理类或者代理类的对象的创建比较麻烦,而且不确定它是否会被使用,那么可以选择使用这种lazy
模式来延迟生成代理。
然后是Dispatcher
,Dispatcher
和LazyLoader
接口相同,也是提供了loadObject()
方法,这个方法同样地返回一个代理对象,这个对象同样可以代理原方法的调用。不过它们之间不同的地方在于,Dispatcher
的loadObject()
方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。也就是说Dispatcher
的loadObject()
方法返回的对象并不会被存储起来,可以类比成Spring中的Prototype
类型,而LazyLoader
则是lazy
模式的Singleton
。
然后是InvocationHandler
,它的使用方式和MethodInterceptor
差不多:
```
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Person.class);
enhancer.setCallback(new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
System.out.println("Intercept!");
return null;
}
});
Person person = (Person) enhancer.create();
person.sayHello();
}
}
需要注意的一点是,所有对invoke()
方法的参数proxy
对象的方法调用都会被委托给同一个InvocationHandler
,所以可能会导致无限循环。
最后是FixedValue
,它同样也提供了一个loadObject()
方法,不过这个方法返回的不是代理对象,而是原方法调用想要的结果。也就是说,在这个Callback
里面,看不到任何原方法的信息,也就没有调用原方法的逻辑,不管原方法是什么都只会调用loadObject()
并返回一个结果。听起来可能有些没有,但是配合CllbackFilter
可以强制使某个方法返回固定的值,并且带来的开销很小。需要注意的是,如果loadObject()
方法的返回值并不能转换成原方法的返回值类型,那么会抛出类型转换异常。