首页 > 代码库 > 深入剖析动态代理(上)之代理的方式
深入剖析动态代理(上)之代理的方式
关于动态代理,大家显式使用的可能比较少,但是说到Spring的Interceptor、各种各样的事务管理,大家会更熟悉一些,没错,这些在底层实现上,都是使用的动态代理,确切的说,想要为一个类的方法,动态添加功能,比如验证、资源释放、日志处理等,大部分都是借助动态代理。
为了平缓的过渡,先来说一下静态代理。
静态代理
静态代理的思路很简单:把一个真实对象的实例放到代理对象的实例中,然后调用代理对象方法,代理对象的方法调用真实对象的方法,以事务管理为例,如下:
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动态代理的源码介绍的,写着写着就写成介绍代理都有哪些类型及实现方式了,再写篇幅就有点长了,所以放到下篇博客说明。
深入剖析动态代理(上)之代理的方式
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。