原文:http://blog.csdn.net/liutengteng130/article/details/46565309
动态代理是指在运行时,动态生成代理类。代理类的字节码将在运行时生成并载入当前的ClassLoader.
生成动态代理类的方法很多,如JDK自带的动态代理、CGLIB、Javassist或者ASM库。
JDK动态代理使用简单,它内置在JDK中,因此不需要引入第三方Jar包,但相对功能比较弱。CGLIB和Javassist都是高级的字节码生成库,总体性能比JDK自带的动态代理好,而且功能十分强大。ASM是低级的字节码生成工具,使用ASM已经近乎在于使用Javabytecode编程,对开发人员要求较高,也是性能最好的一种动态代理生辰工具。但ASM的使用是在过于繁琐,而且性能也没有数量级的提升,与CGLIB等高级字节码生成工具相比,ASM程序的可维护性也较差。
JDK实现
1、步骤
1)通过实现InvocationHandler接口创建自己的调用处理器
2)通过为Proxy类指定ClassLoader对象和一组interface来创建动态代理类
3)通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
4)通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入
2、创建代理
-
-
- InvocationHandler handler = new InvocaitonHandlerImpl(..);
-
-
- Class clazz = Proxy.getProxyClass(classLoader,new Class[]{Interface.class,...});
-
-
- Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
-
-
- Interface Proxy = (Interface)constructor.newInstance(new Object[]{handler});
-
-
-
- InvocaitonHandler handler = new InvocationHandlerImpl(..);
-
-
- nterface proxy = (Interface)Proxy.newProxyInstance(classLoader,new Class[]{Interface.class},handler);
3、代码
-
-
-
-
-
- public interface IDBQuery {
-
- String request();
- }
-
-
-
-
-
-
- public class DBQuery implements IDBQuery {
-
-
- public DBQuery(){
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public String request() {
-
- return "request string";
- }
-
- }
-
-
-
-
-
-
- public class JdkDbQueryHandler implements InvocationHandler{
-
- IDBQuery real = null;
-
-
-
-
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- if(real == null)
- real = new DBQuery();
- return real.request();
- }
-
-
-
-
-
- public static IDBQuery createJdkProxy(){
-
-
-
-
-
- IDBQuery jdkProxy = (IDBQuery) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{IDBQuery.class}, new JdkDbQueryHandler());
- return jdkProxy;
- }
-
-
- }
执行过程:
如图,目标对象的方法调用被Proxy拦截,在InvocationHandler中的回调方法中通过反射调用,这种动态代理的方法实现了对类的方法的运行时修改。
JDK动态代理有个缺点,那就是不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。准确的说这不应该是缺点,一个良好的系统,每个类都应该有一个接口与之对应。针对接口编程。
CGLIB实现
- package com.ltt.dynamic;
-
- 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 CglibDbQueryInterceptor implements MethodInterceptor{
- IDBQuery real = null;
-
-
-
-
- @Override
- public Object intercept(Object arg0, Method arg1, Object[] arg2,
- MethodProxy arg3) throws Throwable {
- if (real == null) {
- real = new DBQuery();
- return real.request();
- }
- return null;
- }
-
-
-
-
-
- public static IDBQuery createCglibProxy(){
- Enhancer enhancer = new Enhancer();
-
- enhancer.setCallback(new CglibDbQueryInterceptor());
-
- enhancer.setInterfaces(new Class[]{IDBQuery.class});
-
- IDBQuery cglibProxy = (IDBQuery) enhancer.create();
- return cglibProxy;
- }
-
-
- }
Javassist实现
一种是使用代理工厂创建,另一种通过使用动态代码创建。使用代理工厂创建时,方法与CGLIB类似,也需要实现一个用于代理逻辑处理的Handler:例如createJavassistDynProxy();使用动态代码创建,生成字节码,这种方式可以非常灵活,甚至可以在运行时生成业务逻辑,如createJavassistBytecodeDynamicProxy()方法。
-
-
-
-
-
- public class JavassistDynDbQueryHandler implements MethodHandler{
-
- IDBQuery real = null;
-
-
-
-
-
- @Override
- public Object invoke(Object arg0, Method arg1, Method arg2, Object[] arg3)
- throws Throwable {
- if (real == null)
- real = new DBQuery();
- return real.request();
-
- }
-
-
-
-
-
-
- public static IDBQuery createJavassistDynProxy() throws Exception{
- ProxyFactory proxyFactory = new ProxyFactory();
- proxyFactory.setInterfaces(new Class[]{IDBQuery.class});
- Class proxyClass = proxyFactory.createClass();
- IDBQuery javassistProxy = (IDBQuery) proxyClass.newInstance();
- ((ProxyObject) javassistProxy).setHandler(new JavassistDynDbQueryHandler());
- return javassistProxy;
- }
-
-
-
-
-
-
- public static IDBQuery createJavassistBytecodeDynamicProxy() throws Exception{
- ClassPool mPool = new ClassPool(true);
-
- CtClass mCtc = mPool.makeClass(IDBQuery.class.getName() + "JavaassistBytecodeProxy");
-
- mCtc.addInterface(mPool.get(IDBQuery.class.getName()));
-
- mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
-
-
- mCtc.addField(CtField.make("public" + IDBQuery.class.getName() + "real;", mCtc));
- String dbQueryname = DBQuery.class.getName();
-
- mCtc.addMethod(CtNewMethod.make("public String request() { if(real==null) real = new " +dbQueryname+"(); return real.request();}", mCtc));
-
-
- Class pc = mCtc.toClass();
-
- IDBQuery bytecodeProxy = (IDBQuery) pc.newInstance();
- return bytecodeProxy;
- }
-
- }
三种动态代理:
JDK
动态代理创建速度优于
CGLIB
动态代理,但是在函数的调用性能上远不如
CGLIB
和
Javassist
。故CGLIB和Javassist整体性能上比JDK动态代理好。
性能比较:
- Create JDK Proxy: 13 ms
- Create CGLIB Proxy: 217 ms
- Create JAVAASSIST Proxy: 99 ms
- Create JAVAASSIST Bytecode Proxy: 168 ms
- ================
- Run JDK Proxy: 2224 ms, 634,022 t/s
- Run CGLIB Proxy: 1123 ms, 1,255,623 t/s
- Run JAVAASSIST Proxy: 3212 ms, 438,999 t/s
- Run JAVAASSIST Bytecode Proxy: 206 ms, 6,844,977 t/s
- ----------------
- Run JDK Proxy: 2169 ms, 650,099 t/s
- Run CGLIB Proxy: 1059 ms, 1,331,506 t/s
- Run JAVAASSIST Proxy: 3328 ms, 423,697 t/s
- Run JAVAASSIST Bytecode Proxy: 202 ms, 6,980,521 t/s
- ----------------
- Run JDK Proxy: 2174 ms, 648,604 t/s
- Run CGLIB Proxy: 1032 ms, 1,366,342 t/s
- Run JAVAASSIST Proxy: 3119 ms, 452,088 t/s
- Run JAVAASSIST Bytecode Proxy: 207 ms, 6,811,910 t/s
- ----------------
Javassist字节码最快,CGLIB次之,是JDK的两倍。
它们都是通过字节码生成来实现动态代理的。只不过是它们生成的字节码不一样,像JDK,CGLIB都考虑了很多因素,以及继承或包装了自己的一些类,所以生成的字节码非常大,而我们很多时候用不上这些,手工生成的字节码非常小(Javassist是手工生成的字节码),所以速度快。
另外,ASM也是手工生成的字节码,速度也很快,但是它没有一个数量级,通常情况下选用Javassist生成字节码的方式。