设计模式 -- 代理模式 静态代理和动态代理

发布时间:2022-03-01 11:53:41 作者:yexindonglai@163.com 阅读(60)

 

代理模式的定义:

    代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用,可以理解为是托管的作用,就像手机里的欢乐斗地主一样,我可以自己决定打什么牌,也可以用托管的方式让电脑帮我决定打什么牌,这时候电脑就帮我做了很多事情,我只需要像葛优一样躺在那静静地看着就好了,这种托管的形式就叫做代理模式;

   

    在电脑程序中,一个对象创建后交给代理模式去管理,除了程序原有的逻辑,代理类还会为这个对象增加额外的逻辑处理,简单地说就是在不改变原代码的情况下来达到扩展功能的目的,一个简单的代理模式结构示意图如下

 

代理模式使用场景

    面向切面编程(springAOP) 、事务管理、日志打印、权限控制、安全代理 可以隐蔽真实角色

 

代理模式的分类

静态代理

    静态代理其实就是在执行方法的前后加上前置和后置逻辑代码!因为是静态,所以只能在一个地方加,想要在多个地方加上代理,就得copy 多份代码到不同的地方,维护成本高,并且开发繁琐,目前静态代理基本上都已经不用了,用的都是动态代理,但我们还是要知道什么是静态代理,因为你只有知道了静态代理,再去理解动态代理才会容易得多;

静态代理使用方法

  1. package com.designPatterm.proxy;
  2. /**
  3. * 设计模式:代理模式 -- 静态代理
  4. */
  5. public class StaticProxy {
  6. public static void main(String[] args) {
  7. UserDao dao = new UserDaoImpl();
  8. // 实例化代理类
  9. DaoProxy daoProxy = new DaoProxy(dao);
  10. daoProxy.add();
  11. daoProxy.delete();
  12. }
  13. }
  14. // dao 类--操作数据库的结论
  15. interface UserDao {
  16. // 添加数据
  17. void add();
  18. // 删除数据
  19. void delete();
  20. }
  21. // dao的实现类
  22. class UserDaoImpl implements UserDao{
  23. @Override
  24. public void add() {
  25. System.out.println("添加数据");
  26. }
  27. @Override
  28. public void delete() {
  29. System.out.println("删除数据");
  30. }
  31. }
  32. // 代理类
  33. class DaoProxy{
  34. public DaoProxy(UserDao userDao){
  35. this.userDao = userDao;
  36. }
  37. private UserDao userDao;
  38. //在add 方法的前后加上想要执行的代码
  39. public void add(){
  40. System.out.println("add方法之前执行的代码--开启事务");
  41. userDao.add();
  42. System.out.println("add方法之后执行的代码-- 关闭事务、提交、回滚");
  43. }
  44. //在delete 方法的前后加上想要执行的代码
  45. public void delete(){
  46. System.out.println("delete方法之前执行的代码--开启事务");
  47. userDao.delete();
  48. System.out.println("delete方法之后执行的代码-- 关闭事务、提交、回滚");
  49. }
  50. }

打印结果

  1. add方法之前执行的代码--开启事务
  2. 添加数据
  3. add方法之后执行的代码-- 关闭事务、提交、回滚
  4. delete方法之前执行的代码--开启事务
  5. 删除数据
  6. delete方法之后执行的代码-- 关闭事务、提交、回滚
  7. Process finished with exit code 0

 

动态代理

    静态代理就相当于是写死的代码,而动态则是活的、可变的,既然是可变就意味着我们可以通过动态修改对象的内存来达到我们想要的功能,所以表面上看代理是没有任何变化的,因为对象的修改已经深入到了最底层(字节码);通常情况下,实现动态代理的方式主要有以下几种:

  •     JDK 动态代理  : JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理,如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了
  •     CGLIB  :GLIB是一个强大的、高性能的代码生成库,底层有ASM实现,CGLIB代理主要通过对字节码的操作,通过继承的方式,对原来的类生成了一个子类,覆盖了需要执行的方法,因为是用继承的方式实现的动态代理,所以动态代理的类一定不要设置成 final ,因为final会阻止继承和多态;jdk动态代理只能对接口进行代理,但是cglib没有这个限制,cglib不使用接口也能实现动态代理;spring的大多数功能就是有cglib实现的
  •     javassist :一个开源的分析、编辑和创建Java字节码的类库,可以直接编辑和生成Java生成的字节码;

 

CGLIB动态代理与JDK动态区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

 

JDK动态代理使用例子

  1. package com.designPatterm.proxy;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. /**
  6. * 动态代理模式--- 使用JDK自带动态代理实现,其实就是反射
  7. * <p>
  8. * 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
  9. */
  10. public class DynamicProxyJdk implements InvocationHandler {
  11. private Object target = null;// 目标代理对象
  12. // 构造方法
  13. public DynamicProxyJdk(Object target) {
  14. this.target = target;
  15. }
  16. /**
  17. * 调用add() 方法时,实际上调用的是invoke 的方法因为这个方法已经将以前的add方法给覆盖了
  18. * @param proxy 被代理的类对象
  19. * @param method 被代理的类方法
  20. * @param args 被代理的类方法参数
  21. * @return
  22. * @throws Throwable
  23. */
  24. @Override
  25. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  26. // 开启事物
  27. System.out.println("动态代理-开启事物");
  28. Object invoke = method.invoke(target, args);
  29. System.out.println("动态代理-提交事物");
  30. return invoke;
  31. }
  32. public static void main(String[] args) {
  33. Dao dao = new DaoImpl();
  34. DynamicProxyJdk dynamicProxyJdk = new DynamicProxyJdk(dao);
  35. //获取类加载器
  36. Class<? extends Dao> daoClass = dao.getClass();
  37. ClassLoader classLoader = daoClass.getClassLoader();
  38. //获取当前实现的接口
  39. Class<?>[] interfaces = daoClass.getInterfaces();
  40. //调用动态代理实例
  41. Dao newDao = (Dao) Proxy.newProxyInstance(classLoader, interfaces, dynamicProxyJdk);
  42. newDao.add();
  43. newDao.sbb(1,2);
  44. }
  45. }
  46. // 被代理接口
  47. interface Dao {
  48. void add();
  49. void sbb(int i,int j);
  50. }
  51. // 被代理的实现类
  52. class DaoImpl implements Dao {
  53. @Override
  54. public void add() {
  55. System.out.println("我是add方法");
  56. }
  57. @Override
  58. public void sbb(int i, int j) {
  59. System.out.println("i+j的结果为:"+i + j);
  60. }
  61. }

打印结果

  1. 动态代理-开启事物
  2. 我是add方法
  3. 动态代理-提交事物
  4. 动态代理-开启事物
  5. i+j的结果为:12
  6. 动态代理-提交事物
  7. Process finished with exit code 0

 

CGLIB动态代理底层原理图

CGLIB动态代理使用例子

  1. package com.designPatterm.proxy;
  2. import com.sun.source.tree.MethodInvocationTree;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6. import net.sf.cglib.proxy.Proxy;
  7. import sun.reflect.MethodAccessor;
  8. import java.lang.reflect.Method;
  9. /**
  10. * 动态代理模式--- 使用CGLIB 字节码技术实现动态代理
  11. */
  12. public class DynamicProxyCglib {
  13. public static void main(String[] args) {
  14. CglbProxy cglbProxy = new CglbProxy();
  15. Object instance = cglbProxy.getInstance(new DynamicProxyCglib());
  16. DynamicProxyCglib DynamicProxyCglib = (DynamicProxyCglib) instance;
  17. // 以下这2个方法都会有前后通知
  18. DynamicProxyCglib.show();
  19. DynamicProxyCglib.add();
  20. }
  21. public void show(){
  22. System.out.println("操作数据。。。。");
  23. }
  24. public void add(){
  25. System.out.println("add方法。。。。");
  26. }
  27. }
  28. /**
  29. * 生成代理类
  30. */
  31. class CglbProxy implements MethodInterceptor {
  32. // 需要代理的对象,这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
  33. private Object targetObject = null;
  34. /**
  35. * 获取代理好的对象
  36. * @param target 需要代理的对象
  37. * @return
  38. */
  39. public Object getInstance(Object target){
  40. this.targetObject = target;
  41. // 通过继承重写的方式来实现动态代理
  42. Enhancer enhancer = new Enhancer();
  43. enhancer.setSuperclass(target.getClass()); // 设置代理目标对象,父类的class
  44. enhancer.setCallback(this); //设置回调为当前对象,当然,也可以像下面一样这么写
  45. /******* 回调的另一种写法 begin ******************************************************/
  46. // enhancer.setCallback(new MethodInterceptor() {
  47. // @Override
  48. // public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  49. // System.out.println("开启事务"); // 前置通知
  50. // // 执行主体方法
  51. // Object invoke = methodProxy.invoke(targetObject, objects);
  52. // System.out.println("关闭事务"); // 后置通知
  53. // return invoke;
  54. // }
  55. // });
  56. /******* 回调的另一种写法 end ******************************************************/
  57. return enhancer.create(); // 创建目标对象的子类实现动态代理;
  58. }
  59. @Override
  60. public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  61. System.out.println("开启事务"); // 前置通知
  62. // 执行主体方法
  63. Object invoke = methodProxy.invoke(targetObject, objects);
  64. System.out.println("关闭事务"); // 后置通知
  65. return invoke;
  66. }
  67. }

打印结果

  1. 开启事务
  2. 操作数据。。。。
  3. 关闭事务
  4. 开启事务
  5. add方法。。。。
  6. 关闭事务
  7. Process finished with exit code 0

 

关键字设计模式