深入剖析动态代理(上)之代理的方式
Posted jzssuanfa
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入剖析动态代理(上)之代理的方式相关的知识,希望对你有一定的参考价值。
关于动态代理,大家显式使用的可能比較少,可是说到Spring的Interceptor、各种各样的事务管理,大家会更熟悉一些,没错,这些在底层实现上,都是使用的动态代理,确切的说,想要为一个类的方法,动态加入功能,比方验证、资源释放、日志处理等,大部分都是借助动态代理。
为了平缓的过渡,先来说一下静态代理。
静态代理
静态代理的思路非常easy:把一个真实对象的实例放到代理对象的实例中。然后调用代理对象方法,代理对象的方法调用真实对象的方法,以事务管理为例。例如以下:
UserDao
package com.tgb.staticproxy; public interface UserDao { public void add(); public void deleteAll(); }
UserDaoImpl
package com.tgb.staticproxy; public class UserDaoImpl implements UserDao { public void add() { System.out.println("加入一名用户到数据库"); } public void deleteAll() { System.out.println("删除全部用户"); } }
UserDaoProxy
package com.tgb.staticproxy; public class UserDaoProxy implements UserDao { UserDao userDao=null; public UserDaoProxy(UserDao userDao) { this.userDao=userDao; } public void add() { System.out.println("开启本地事务"); userDao.add(); System.out.println("提交或回滚事务"); } public void deleteAll() { System.out.println("开启本地事务"); userDao.deleteAll(); System.out.println("提交或回滚事务"); } }
Test
package com.tgb.staticproxy; public class Test { /** * @param args */ public static void main(String[] args) { UserDao userDao=new UserDaoImpl(); UserDaoProxy userDaoProxy=new UserDaoProxy(userDao); //測试加入 userDaoProxy.add(); System.out.println("..........分隔符.........."); //測试删除 userDaoProxy.deleteAll(); } }
运行结果
开启本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 开启本地事务 删除全部用户 提交或回滚事务
可是静态代理管理事务的方式问题非常大,每一个Dao类的每一个方法都须要开启和关闭事务,不仅代码反复严重,而事务本来是和业务没什么关联,却耦合到一起。
动态代理
JDK动态代理
相同以事务管理为例,例如以下:
UserDao
package com.tgb.dynamicproxy; public interface UserDao { public void add(); public void deleteAll(); }
UserDaoImpl
package com.tgb.dynamicproxy; public class UserDaoImpl implements UserDao { @Override public void deleteAll() { System.out.println("删除全部用户信息"); } @Override public void add() { System.out.println("加入一名用户到数据库"); } }
Handler
package com.tgb.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class Handler implements InvocationHandler { private Object target; public Handler(Object target) { this.target=target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //开启事务 before(); //运行业务 method.invoke(target, args); //提交或回滚事务 after(); return null; } public void before() { System.out.println("開始本地事务"); } public void after() { System.out.println("提交或回滚事务"); } }
Test
package com.tgb.dynamicproxy; import java.lang.reflect.Proxy; public class Test { /** * @param args */ public static void main(String[] args) { try{ UserDao impl=new UserDaoImpl(); Handler handler=new Handler(impl); UserDao proxy=(UserDao)Proxy.newProxyInstance (impl.getClass().getClassLoader(), impl.getClass().getInterfaces(), handler); //測试加入 proxy.add(); System.out.println("..........分隔符.........."); //測试删除 proxy.deleteAll(); } catch(Exception e) { e.printStackTrace(); } } }
运行结果
開始本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 開始本地事务 删除全部用户信息 提交或回滚事务
JDK的动态代理克服了静态代理耦合和代码反复的问题,可是JDK的代理模式有个比較严重的问题。如UserDao必需要有接口才干够使用JDK动态代理,这就大大限制了JDK动态代理的范围。
cglib动态代理
asm能够动态生成字节码,cglib对asm进行了再封装,cglib并非为了动态代理而生的,可是利用它的特性。却能够非常好的实现动态代理。
UserDaoImpl
package com.tgb.cglib; public class UserDaoImpl { public void deleteAll() { System.out.println("删除全部用户信息"); } public void add() { System.out.println("加入一名用户到数据库"); } }
CglibProxy
package com.tgb.cglib; import java.lang.reflect.Method; import net.sf.cglib.proxy.Callback; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; public class CglibProxy implements MethodInterceptor { private Object target; private CglibProxy(Object target){ this.target = target; } //产生代理对象 @SuppressWarnings("unchecked") public static <T> T proxyTarget(T t){ Enhancer en = new Enhancer(); en.setSuperclass(t.getClass()); en.setCallback((Callback) new CglibProxy(t)); T tt = (T) en.create(); return tt; } //运行拦截 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("开启本地事务"); Object o = method.invoke(target, args); System.out.println("提交或回滚事务"); return o; } }
Test
package com.tgb.cglib; public class Test { /** * @param args */ public static void main(String[] args) { //获代替理对象 UserDaoImpl impl=CglibProxy.proxyTarget(new UserDaoImpl()); //測试加入 impl.add(); System.out.println("..........分隔符.........."); //測试删除 impl.deleteAll(); } }
执行结果
开启本地事务 加入一名用户到数据库 提交或回滚事务 ..........分隔符.......... 开启本地事务 删除全部用户信息 提交或回滚事务
能够看到,这次UserDaoImpl并没有实现不论什么接口接口实现动态代理的功能。
总结
这篇博客本来打算写JDK和cglib动态代理的源代码介绍的。写着写着就写成介绍代理都有哪些类型及实现方式了,再写篇幅就有点长了。所以放到下篇博客说明。
以上是关于深入剖析动态代理(上)之代理的方式的主要内容,如果未能解决你的问题,请参考以下文章
Java深入浅出系列——深入剖析动态代理--从静态代理到动态代理的演化
JDK动态代理[2]----JDK动态代理的底层实现之Proxy源码分析