CGLib动态代理
使用JDK创建代理有一个限制,即它只能为接口创建代理,这一点我们从Proxy的接口方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)就看得很清楚,第三个入参interfaces就是为代理实例指定的实现接口。虽然,面向接口的编程被很多很有影响力人(包括Rod Johnson)的推崇,但在实际开发中,开发者也遇到了很多困惑:难道对一个简单业务表的操作真的需要创建5个类(领域对象类、Dao接口,Dao实现类,Service接口和Service实现类)吗?对于这一问题,我们还是留待大家进一步讨论。现在的问题是:对于没有通过接口定义业务方法的类,如何动态创建代理实例呢?JDK的代理技术显然已经黔驴技穷,CGLib作为一个替代者,填补了这个空缺。你可以从http://cglib.sourceforge.net/获取CGLib的类包,也可以直接从Spring的关联类库lib/cglib中获取类包。
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,并在拦截方法相应地织入横切逻辑。下面,我们采用CGLib技术,编写一个可以为任何类创建织入性能监视横切逻辑的代理对象的代理器,如代码清单 8所示:
代码清单 8 CglibProxy
package com.baobaotao.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz); ① 设置需要创建子类的类
enhancer.setCallback(this);
return enhancer.create(); ②通过字节码技术动态创建子类实例
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
Object result=proxy.invokeSuper(obj, args); ③ 通过代理类调用父类中的方法
PerformanceMonitor.end();
return result;
}
}
在上面代码中,你可以通过getProxy(Class clazz)为一个类创建动态代理对象,该代理对象是指定类clazz的子类。在这个代理对象中,我们织入性能监视的横切逻辑(粗体部分)。intercept(Object obj, Method method, Object[] args,MethodProxy proxy)是CGLib定义的Inerceptor接口的方法,obj表示父类的实例,method为父类方法的反射对象,args为方法的动态入参,而proxy为代理类实例。
下面,我们通过CglibProxy为ForumServiceImpl类创建代理对象,并测试代理对象的方法,如代码清单 9所示:
代码清单 9 TestForumService:测试Cglib创建的代理类
package com.baobaotao.proxy;
import java.lang.reflect.Proxy;
public class TestForumService {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
ForumServiceImpl forumService = //① 通过动态生成子类的方式创建代理对象
(ForumServiceImpl )proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}
}
在①中,我们通过CglibProxy为ForumServiceImpl动态创建了一个织入性能监视逻辑的代理对象,并调用了代理对象的业务方法。运行上面的代码,输入以下的信息:
begin monitor...
模拟删除Forum记录:10
end monitor...
com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0.removeForum花费47毫秒。
begin monitor...
模拟删除Topic记录:1023
end monitor...
com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0.removeTopic花费16毫秒。
观察以上的输出,除了发现两个业务方法中都织入了性能监控的逻辑外,我们还发现代理类的名字是com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0,这个特殊的类就是CGLib为ForumServiceImpl所动态创建的子类。
package com.baobaotao.proxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
![]()
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
enhancer.setSuperclass(clazz); ① 设置需要创建子类的类
enhancer.setCallback(this);
return enhancer.create(); ②通过字节码技术动态创建子类实例
}
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName());
Object result=proxy.invokeSuper(obj, args); ③ 通过代理类调用父类中的方法
PerformanceMonitor.end();
return result;
}
}
![]()
![]()
在上面代码中,你可以通过getProxy(Class clazz)为一个类创建动态代理对象,该代理对象是指定类clazz的子类。在这个代理对象中,我们织入性能监视的横切逻辑(粗体部分)。intercept(Object obj, Method method, Object[] args,MethodProxy proxy)是CGLib定义的Inerceptor接口的方法,obj表示父类的实例,method为父类方法的反射对象,args为方法的动态入参,而proxy为代理类实例。
下面,我们通过CglibProxy为ForumServiceImpl类创建代理对象,并测试代理对象的方法,如代码清单 9所示:
代码清单 9 TestForumService:测试Cglib创建的代理类
package com.baobaotao.proxy;
import java.lang.reflect.Proxy;
public class TestForumService {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
ForumServiceImpl forumService = //① 通过动态生成子类的方式创建代理对象
(ForumServiceImpl )proxy.getProxy(ForumServiceImpl.class);
forumService.removeForum(10);
forumService.removeTopic(1023);
}
}
![]()
在①中,我们通过CglibProxy为ForumServiceImpl动态创建了一个织入性能监视逻辑的代理对象,并调用了代理对象的业务方法。运行上面的代码,输入以下的信息:
begin monitor...
模拟删除Forum记录:10
end monitor...
com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0.removeForum花费47毫秒。
begin monitor...
模拟删除Topic记录:1023
end monitor...
com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0.removeTopic花费16毫秒。
![]()
观察以上的输出,除了发现两个业务方法中都织入了性能监控的逻辑外,我们还发现代理类的名字是com.baobaotao.proxy.ForumServiceImpl$$EnhancerByCGLIB$$2a9199c0,这个特殊的类就是CGLib为ForumServiceImpl所动态创建的子类。