[步伐计划]—署理模式

[复制链接]
发表于 2025-11-10 07:43:27 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
[步伐计划]—署理模式👳

本文章记载学习于——52.面向切面:AOP-场景模拟_哔哩哔哩_bilibili
迩来闲来无事,在学习Spring的源码: 背面逐步更新源码系列blog,渴望多多关注🙏🙏
现在已经总结的blog 系列🕳: 慈様や 前端学习导航👩🏻‍🚀🚀 、小様—Java后端开辟日记
很多框架在计划和实现过程中广泛运用了多种计划模式:Spring焦点IOC、AOP


  • 工厂模式: BeanFactory 和 ApplicationContext 实现了工厂模式,负责创建和管理Bean对象
  • 单例模式: Spring 默认 将全部Bean界说为单例模式,即每个容器中只存在一个实例
  • 署理模式: AOP 面向切面编程的焦点,通过动态署理:JDK署理、CGLIB署理
    实现了非侵入式编程,可以在不修改原有业务代码的环境下增长额外功能
    在目的方法调用前后添加增强逻辑,如变乱管理、日记记载等,
  • 适配器默认、观察者默认、战略模式… 计划模式使,
    Spring框架显现出了高度的模块化、可设置性和可扩展性,使得开辟者可以大概高效地构建复杂的应用步伐
👊 总而颜值->计划模式很告急,
计划模式实在是在软件开辟过程中颠末履历积累和验证总结,一套通用代码计划方案
如果认识了计划模式,当遇到类似的场景,我们可以快速地参考计划模式实当代码,
不光可以加快我们的编码速率,也提拔了代码的:扩展性、重用性、维护性!
个人见解:
固然,计划模式听着高大上,实在现实开辟过程中已经潜移默化的打仗了很多了,
且有的计划模式已经随着期间、或还并没有完全适当的引用常见,新手发起会用为主,相识为辅
计划模式:属于步伐的内功心法💖、学习是一个长期反复推敲的过程、不绝优化升级🆙🆙❗ ❗ ❗


  • 留意:计划模式不属于某个特定的编程语言,而是一种编程头脑,代码模式;
计划模式分类:

现在计划模式重要有:23种,大抵分为三大类:
创建型模式 Creational Patterns 用于创建对象的模式,同时隐蔽对象创建的逻辑,
克制代码中出现大量 new 利用和复杂的创建逻辑,目的是解耦对象的创建和使用


  • 常用的有: 单例模式、工厂模式、制作者模式
  • 不常见的有: 原型模式
布局型模式 Structural Patterns
用于处置处罚对象组合的布局,关注类与对象的组合,目的是通过组合对象或类的方式,形成更大的布局


  • 常用的有: 适配器模式、桥接模式、组合模式、装饰器模式、署理模式
  • 不常见的有: 表面模式(门面模式)、享元模式
举动型模式Behavioral Patterns
用于界说对象相互协作以完成单个对象无法单独实现的任务,目的:是界说类和对象间的通讯方式;


  • 常用的有: 观察者模式、责任链模式、战略模式、模板模式、状态模式、迭代器模式,
  • 不常见的有: 备忘录模式、下令模式、中介者模式、访问者模式、表明器模式
需求先容:

现在有一个盘算器🧮类,它有add 加、sub 减 的方法,新需求: 盘算前后举行日记记载输出;
声明盘算器接口: 用于规范盘算器类的界说;
  1. //计算器类接口:
  2. public interface Calculator {
  3.     //计算器加函数
  4.     int add(int i,int j);
  5.     //计算器减函数
  6.     int sub(int i,int j);
  7. }
复制代码
盘算器接口实现类: 声明并实现盘算器接口;
  1. //计算器接口实现类:
  2. public class CalculatorImpl implements Calculator {
  3.     @Override
  4.     public int add(int i, int j) { return i+j; }
  5.     @Override
  6.     public int sub(int i, int j) { return i-j; }
  7. }
复制代码
新需求:在加减之进步行日记记载——盘算器日记接口实现类:


  • 正常环境下,大部分开辟者会直接在上面实现类上改动,但为了不影响其他就创建新的实现类;
  1. //计算器日志日志接口实现类: 不就是日志日志嘛一行代码CV的事~~
  2. public class CalculatorLogImpl implements Calculator {
  3.     @Override
  4.     public int add(int i, int j) {
  5.         System.out.println("[日志日志]mul 方法开始了,参数是:"+i+","+ j);
  6.         System.out.println("[日志]mul 方法结束了,结果是:"+(i + j));
  7.         return i + j;
  8.     }
  9.     @Override
  10.     public int sub(int i, int j) {
  11.         System.out.println("[日志]mul 方法开始了,参数是:"+i+","+ j);
  12.         System.out.println("[日志]mul 方法结束了,结果是:"+(i - j));
  13.         return i-j;
  14.     }
  15. }
复制代码

轻轻松松~ 小活😀 这就竣事了吗?到此为止了…
NO—NO—NO❗ ❗ ❗ 如果这个类在大一点呢?大概方法实现更复杂呢?


  • 针对日记功能实现类,我们发现有如下缺陷:对焦点业务功能有干扰,附加功能分散各个业务功能中;
🆗,主角登场——署理模式:👇👇👇
JDK 署理模式

署理模式是一种布局型计划模式:
它为其他对象提供一种署理,以控制对这个对象的访问,署理对象可以在客户端和目的对象之间起到中介的作用,
在不改变目的对象接口的条件下,对目的对象的访问举行控制或增强,上述代码举例:👆👆👆
署理模式的特点:


  • 功能增强: 署理对象可以在访问现实对象之前或之后添加额外的功能
  • 间接访问: 客户端通过署理访问现实对象,署理对象负责对现实对象的控制
  • 代码解耦: 客户端不直接与现实对象交互,通过署理对象可以透明地扩显现实对象的功能
署理模式的分类:


  • 静态署理: 在编译时创建署理类,署理类与目的类实现类似的接口,
  • 动态署理: 在运行时动态天生署理类,实用于无需事先界说署理类的场景
静态署理:

静态署理:不保举) 相识即可,现实开辟中不常常使用,
署理类和被署理类在 编译时期 就确定了它们之间的署理关系,
署理类必要 实现 与被署理类 类似的接口 ,而且在署理类中持有被署理类的实例 目的对象,
通过调用被署理类的方法,来完成现实的利用,在方法调用前后添加额外的逻辑; 这一点也 不高级;
  1. //静态代理类
  2. public class CalculatorStaticProxy implements Calculator {
  3.     //被代理的目标对象
  4.     private Calculator target;
  5.     public CalculatorStaticProxy(Calculator target) { this.target = target; }
  6.           @Override
  7.                 //附加功能由代理类中的代理方法来实现
  8.     public int add(int i, int j) {
  9.         System.out.println("[日志] add 方法开始了,参数是:" + i + "," + j);
  10.       
  11.         //通过目标对象来实现核心业务逻辑
  12.         int addResult = target.add(i, j);
  13.       
  14.         System.out.println("[日志] add 方法结束了,结果是:" + addResult);
  15.         return addResult;
  16.     }
  17.   //省略...其他实现;
  18. }
复制代码
  1. //静态代理使用:
  2. public class CalculatorStaticProxyMain {
  3.     public static void main(String[] args) {
  4.         //创建被代理类对象
  5.         CalculatorImpl calculator = new CalculatorImpl();
  6.         //被代理类对象 ==>构造创建 对应的 ==> 静态代理类,静态代理类中对函数进行调用扩展;
  7.         CalculatorStaticProxy calStaticProxy = new CalculatorStaticProxy(calculator);
  8.         //因此: 直接使用静态代理类对象即可,获得存在日志的计算机函数方法;
  9.         calStaticProxy.add(1,1);
  10.     }
  11. }
复制代码

静态署理确实实现相识耦:,但是由于代码都写死了,完全不具备任何的机动性


  • 如果:未来其他地方也必要附加日记,那还得再声明更多个静态署理类,那就产生了大量重复的代码
怎样: 将日记功能会集到一个署理类中,未来有任何日记需求,都通过这一个署理类来实现——动态署理技能
动态署理:

涉及知识: 【反射】、【类加载ClassLoad】… ,JDK 官方文档📑 Java.lang.reflect.Proxy
Java 动态署理是一种在运行时 动态地创建署理对象的机制:


  • 它允许开辟者在不修改原有类代码的根本上,
  • 为目的对象创建署理,而且可以大概在署理对象的方法调用前后机动地添加自界说的逻辑;
与静态署理差别: 动态署理不必要为每个目的类手动编写署理类,在JDK中,常用的实现方式是 反射


  • 反射机制: 是指步伐在运行期间可以访问、检测和修改其本身状态或举动的一种本事
实现原理

实现原理: 基于 Java 的java.lang.reflect包:InvocationHandler接口:
开辟者必要实现这个接口,而且实现invoke方法,在invoke方法中界说署理对象的方法被调用时要实行逻辑;


  • invoke方法有三个参数: proxy署理对象本身、method被调用的方法对象、args方法的参数数组;
Proxy类: 通过Proxy.newProxyInstance 方法可以 创建天生 署理对象;这个方法必要三个参数:


  • 目的对象的类加载器:目的对象.getClass().getClassLoader(),
  • 目的对象的接口数组:目的对象.getClass().getInterfaces(),
  • InvocationHandler 接口实例;
🆗,到此就得到了一个:(动态)署理对象: 使用动态署理对象,具有目的对象,一样的方法([参数]);


  • 通过署理对象方法时,现实上会调用InvocationHandler实例对象的invoke方法,方法中,实行了增强逻辑;

  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. //动态代理类
  4. public class DynamicProxy implements InvocationHandler {
  5.     //目标代理对象
  6.     private Object target;
  7.     public DynamicProxy(Object target) { this.target = target; }
  8.     @Override
  9.     //proxy代理对象本身、method被调用的方法对象、args方法的参数数组
  10.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  11.         System.out.println("[日志] "+method.getName()+" 方法开始了,参数是:" + args);
  12.         // 通过目标对象来实现核心业务逻辑
  13.         Object addResult = method.invoke(target, args);
  14.         System.out.println("[日志] "+method.getName()+" 方法结束了,结果是:" + addResult);
  15.         return addResult;
  16.     }
  17. }
复制代码
  1. import org.proxyD.Calculator;
  2. import java.lang.reflect.Proxy;
  3. import org.proxyD.CalculatorImpl;
  4. /** 动态代理使用: */
  5. public class DynamicProxyMain {
  6.     public static void main(String[] args) {
  7.         //定义目标对象、InvocationHandler接口实例
  8.         CalculatorImpl calculator = new CalculatorImpl();
  9.         DynamicProxy calStaticProxy = new DynamicProxy(calculator);
  10.         //Proxy.newProxyInstance(目标类加载对象,目标接口数组,InvocationHandler接口实例) 动态创建生成代理对象
  11.         Calculator Calculatorimpl = (Calculator) Proxy.newProxyInstance(
  12.                 //calculator 目标对象通过接口getClass 获得类加载对象、接口数组
  13.                 calculator.getClass().getClassLoader(),
  14.                 calculator.getClass().getInterfaces(),
  15.                 //InvocationHandler 接口实例
  16.                 calStaticProxy
  17.         );
  18.         //使用: 生成代理类
  19.         Calculatorimpl.add(1,1);
  20.         Calculatorimpl.sub(1,1);
  21.     }
  22. }
复制代码

动态署理上风: 可以在运行时根据必要动态地创建署理对象,而不必要像静态署理那样为每个目的类提前编写署理类
代码复用性强:一个InvocationHandler实现类可以用于署理多个实现类似接口的目的对象
易于维护和扩展:由于署理逻辑会集在InvocationHandler实现类的invoke方法中,
当必要修改署理逻辑时,只必要修改这个方法即可,而不必要多个署理类举行修改;
优化—署理工厂:

思量到代码复用与管理: 可以对上述代码举行优化一个 署理工厂,将Proxy.newProxyInstance 天生署理类利用举行同一管理;
匿名内部类实现: InvocationHandler接口,重写了invoke方法;
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. import java.lang.reflect.Proxy;
  4. import java.util.Arrays;
  5. //动态代理工厂
  6. public class ProxyFactory {
  7.     //目标对象
  8.     private Object target;
  9.     public ProxyFactory(Object target) { this.target = target; }
  10.     //动态代理对象函数
  11.     //通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象;
  12.     public Object getProxy(){
  13.                                 /** newProxyInstance():创建一个代理实例 其中有三个参数: */
  14.         /** 1、classLoader:加载动态生成的代理类的类加载器 */
  15.         ClassLoader classLoader = target.getClass().getClassLoader();
  16.         /** 2、interfaces:目标对象实现的所有接口的class对象所组成的数组 */
  17.         Class<?>[] interfaces = target.getClass().getInterfaces();
  18.         /** 3、通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象
  19.          * invocationHandler:设置代理对象实现目标对象方法的过程,即代理类中如何重写接口中的抽象方法 */
  20.         InvocationHandler invocationHandler = new InvocationHandler() {
  21.             @Override
  22.             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  23.                 Object result = null;
  24.                 try {
  25.                     System.out.println("[动态代理][日志] "+method.getName()+",参数:"+ Arrays.toString(args));
  26.                     result = method.invoke(target, args);
  27.                     System.out.println("[动态代理][日志] "+method.getName()+",结果:"+ result);
  28.                 } catch (Exception e) {
  29.                     e.printStackTrace();
  30.                     System.out.println("[动态代理][日志] "+method.getName()+" 异常");
  31.                 } finally {
  32.                     System.out.println("[动态代理][日志] 方法执行完毕");
  33.                 }
  34.                 return result;
  35.             }
  36.         };
  37.         //最后返回代理对象;
  38.         return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
  39.     }
  40. }
复制代码

CGLIB 动态署理:

JDK动态署理是基于接口的,以是要求署理类肯定是有界说接口的,
CGLIB 基于 ASM 字节码天生工具,它是通过 继承的方式天生目的类的子类来实现署理类,


  • 留意 final 方法,不可继承
  • 它们之间的性能随着 JDK 版本的差别而差别:
  • JDK6 在运行次数较少的环境下,动态署理与 CGLIB 差距不显着,次数增长之后 CGLIB 更快
  • JDK7 环境发生了逆转!在运行次数较少1.000,000环境下,JDK 动态署理比 CGLIB 快了差不多30%;
    而当调用次数增长之后(50000000),JDK动态署理比 CGLIB 快了靠近1倍,Jdk8 体现和 Jdk7 根本划一
CGLIB 的工作原理

继承目的类:CGLIB 通过 继承目的类 来创建署理类。在天生的署理类中,它会重写目的类的方法
方法拦截:CGLIB 使用方法拦截器(MethodInterceptor)来控制对目的类方法的访问;


  • 当署理类的方法被调用时,会先辈入方法拦截器的intercept方法;举行利用;
添加 CGLIB 依赖:CGLIB 是第三方库,以是必要引入对应依赖;
  1. <dependency>
  2.     <groupId>cglib</groupId>
  3.     <artifactId>cglib-nodep</artifactId>
  4.     <version>3.3.0</version>
  5. </dependency>
复制代码
创建方法拦截器:实现 CGLIB 的MethodInterceptor接口来界说方法拦截逻辑,重写:intercept方法:


  • 第一个参数o是署理对象本身
  • 第二个参数method是被调用的目的方法
  • 第三个参数objects是目的方法的参数数组
  • 第四个参数methodProxy是一个方法署理对象,用于调用目的类的原始方法
    通过methodProxy.invokeSuper(o, objects)可以调用目的类的原始方法,并获取返回效果;
  1. import net.sf.cglib.proxy.MethodInterceptor;
  2. import net.sf.cglib.proxy.MethodProxy;
  3. import java.lang.reflect.Method;
  4. //实现 CGLIB 的MethodInterceptor接口来定义方法拦截逻辑
  5. public class CglibInterceptor implements MethodInterceptor {
  6.     @Override
  7.     public Object intercept(Object o, Method method,
  8.                             Object[] objects, MethodProxy methodProxy) throws Throwable {
  9.         System.out.println("代理类在目标方法执行前的操作"+method.getName());
  10.         Object result = methodProxy.invokeSuper(o, objects);
  11.         System.out.println("代理类在目标方法执行后结果"+result);
  12.         return result;
  13.     }
  14. }
复制代码
  1. //CGLIB 的Enhancer类来创建代理对象
  2. import net.sf.cglib.proxy.Enhancer;
  3. public class CglibInterceptorMain {
  4.     public static void main(String[] args) {
  5.         //首先创建一个Enhancer对象
  6.         Enhancer enhancer = new Enhancer();
  7.         //设置代理类要继承的目标类、设置方法拦截器
  8.         enhancer.setSuperclass(Calculator.class);
  9.         enhancer.setCallback(new CglibInterceptor());
  10.         //最后通过enhancer.create()创建代理对象将其转换为目标类类型
  11.         Calculator proxy = (Calculator) enhancer.create();
  12.         //代理类对象调用函数;
  13.         proxy.add(1,1);
  14.         proxy.sub(1,1);
  15.     }
  16. }
复制代码

🆗,署理模式大抵云云,是不是so easy轻轻松松的,求点赞👍、收藏⭐ 🥰🥰🥰
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表