[程序设计]—代理模式

发布时间:2026/11/6 6:17:23
[程序设计]—代理模式 [程序设计]—代理模式本文章记录学习于——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 减的方法新需求计算前后进行日志记录输出声明计算器接口用于规范计算器类的定义//计算器类接口:publicinterfaceCalculator{//计算器加函数intadd(inti,intj);//计算器减函数intsub(inti,intj);}计算器接口实现类声明并实现计算器接口//计算器接口实现类:publicclassCalculatorImplimplementsCalculator{Overridepublicintadd(inti,intj){returnij;}Overridepublicintsub(inti,intj){returni-j;}}新需求在加减之前进行日志记录——计算器日志接口实现类正常情况下大部分开发者会直接在上面实现类上改动但为了不影响其他就创建新的实现类//计算器日志接口实现类: 不就是日志嘛一行代码CV的事~~publicclassCalculatorLogImplimplementsCalculator{Overridepublicintadd(inti,intj){System.out.println([日志]mul 方法开始了参数是:i,j);System.out.println([日志]mul 方法结束了结果是:(ij));returnij;}Overridepublicintsub(inti,intj){System.out.println([日志]mul 方法开始了参数是:i,j);System.out.println([日志]mul 方法结束了结果是:(i-j));returni-j;}}轻轻松松~ 小活这就结束了吗到此为止了…NO—NO—NO❗ ❗ ❗如果这个类在大一点呢或者方法实现更复杂呢针对日志功能实现类我们发现有如下缺陷对核心业务功能有干扰附加功能分散各个业务功能中主角登场——代理模式JDK 代理模式代理模式是一种结构型设计模式它为其他对象提供一种代理以控制对这个对象的访问代理对象可以在客户端和目标对象之间起到中介的作用在不改变目标对象接口的前提下对目标对象的访问进行控制或增强上述代码举例代理模式的特点功能增强代理对象可以在访问实际对象之前或之后添加额外的功能间接访问客户端通过代理访问实际对象代理对象负责对实际对象的控制代码解耦客户端不直接与实际对象交互通过代理对象可以透明地扩展实际对象的功能代理模式的分类静态代理在编译时创建代理类代理类与目标类实现相同的接口动态代理在运行时动态生成代理类适用于无需事先定义代理类的场景静态代理静态代理不推荐)了解即可实际开发中不经常使用代理类和被代理类在编译时期就确定了它们之间的代理关系代理类需要实现与被代理类相同的接口并且在代理类中持有被代理类的实例目标对象通过调用被代理类的方法来完成实际的操作在方法调用前后添加额外的逻辑这一点也不高级//静态代理类publicclassCalculatorStaticProxyimplementsCalculator{//被代理的目标对象privateCalculatortarget;publicCalculatorStaticProxy(Calculatortarget){this.targettarget;}Override//附加功能由代理类中的代理方法来实现publicintadd(inti,intj){System.out.println([日志] add 方法开始了参数是i,j);//通过目标对象来实现核心业务逻辑intaddResulttarget.add(i,j);System.out.println([日志] add 方法结束了结果是addResult);returnaddResult;}//省略...其他实现;}//静态代理使用:publicclassCalculatorStaticProxyMain{publicstaticvoidmain(String[]args){//创建被代理类对象CalculatorImplcalculatornewCalculatorImpl();//被代理类对象 构造创建 对应的 静态代理类,静态代理类中对函数进行调用扩展;CalculatorStaticProxycalStaticProxynewCalculatorStaticProxy(calculator);//因此: 直接使用静态代理类对象即可,获得存在日志的计算机函数方法;calStaticProxy.add(1,1);}}静态代理确实实现了解耦但是由于代码都写死了完全不具备任何的灵活性如果将来其他地方也需要附加日志那还得再声明更多个静态代理类那就产生了大量重复的代码如何将日志功能集中到一个代理类中将来有任何日志需求都通过这一个代理类来实现——动态代理技术动态代理涉及知识【反射】、【类加载ClassLoad】… JDK 官方文档Java.lang.reflect.ProxyJava 动态代理是一种在运行时动态地创建代理对象的机制它允许开发者在不修改原有类代码的基础上为目标对象创建代理并且能够在代理对象的方法调用前后灵活地添加自定义的逻辑与静态代理不同动态代理不需要为每个目标类手动编写代理类在JDK中常用的实现方式是反射反射机制是指程序在运行期间可以访问、检测和修改其本身状态或行为的一种能力实现原理实现原理基于 Java 的java.lang.reflect包InvocationHandler接口开发者需要实现这个接口并且实现invoke方法在invoke方法中定义代理对象的方法被调用时要执行逻辑invoke方法有三个参数proxy代理对象本身、method被调用的方法对象、args方法的参数数组Proxy类通过Proxy.newProxyInstance方法可以创建生成代理对象这个方法需要三个参数目标对象的类加载器目标对象.getClass().getClassLoader(),目标对象的接口数组目标对象.getClass().getInterfaces(),InvocationHandler接口实例到此就获得了一个(动态)代理对象使用动态代理对象具有目标对象一样的方法([参数]);通过代理对象方法时实际上会调用InvocationHandler实例对象的invoke方法方法中执行了增强逻辑importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;//动态代理类publicclassDynamicProxyimplementsInvocationHandler{//目标代理对象privateObjecttarget;publicDynamicProxy(Objecttarget){this.targettarget;}Override//proxy代理对象本身、method被调用的方法对象、args方法的参数数组publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{System.out.println([日志] method.getName() 方法开始了参数是args);// 通过目标对象来实现核心业务逻辑ObjectaddResultmethod.invoke(target,args);System.out.println([日志] method.getName() 方法结束了结果是addResult);returnaddResult;}}importorg.proxyD.Calculator;importjava.lang.reflect.Proxy;importorg.proxyD.CalculatorImpl;/** 动态代理使用: */publicclassDynamicProxyMain{publicstaticvoidmain(String[]args){//定义目标对象、InvocationHandler接口实例CalculatorImplcalculatornewCalculatorImpl();DynamicProxycalStaticProxynewDynamicProxy(calculator);//Proxy.newProxyInstance(目标类加载对象,目标接口数组,InvocationHandler接口实例) 动态创建生成代理对象CalculatorCalculatorimpl(Calculator)Proxy.newProxyInstance(//calculator 目标对象通过接口getClass 获得类加载对象、接口数组calculator.getClass().getClassLoader(),calculator.getClass().getInterfaces(),//InvocationHandler 接口实例calStaticProxy);//使用: 生成代理类Calculatorimpl.add(1,1);Calculatorimpl.sub(1,1);}}动态代理优势可以在运行时根据需要动态地创建代理对象而不需要像静态代理那样为每个目标类提前编写代理类代码复用性强一个InvocationHandler实现类可以用于代理多个实现相同接口的目标对象易于维护和扩展由于代理逻辑集中在InvocationHandler实现类的invoke方法中当需要修改代理逻辑时只需要修改这个方法即可而不需要多个代理类进行修改优化—代理工厂考虑到代码复用与管理可以对上述代码进行优化一个 代理工厂将Proxy.newProxyInstance生成代理类操作进行统一管理匿名内部类实现InvocationHandler接口重写了invoke方法importjava.lang.reflect.InvocationHandler;importjava.lang.reflect.Method;importjava.lang.reflect.Proxy;importjava.util.Arrays;//动态代理工厂publicclassProxyFactory{//目标对象privateObjecttarget;publicProxyFactory(Objecttarget){this.targettarget;}//动态代理对象函数//通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象;publicObjectgetProxy(){/** newProxyInstance()创建一个代理实例 其中有三个参数: *//** 1、classLoader加载动态生成的代理类的类加载器 */ClassLoaderclassLoadertarget.getClass().getClassLoader();/** 2、interfaces目标对象实现的所有接口的class对象所组成的数组 */Class?[]interfacestarget.getClass().getInterfaces();/** 3、通过: 创建 InvocationHandler接口 匿名实现类对象,直接返回 动态代理对象 * invocationHandler设置代理对象实现目标对象方法的过程即代理类中如何重写接口中的抽象方法 */InvocationHandlerinvocationHandlernewInvocationHandler(){OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Objectresultnull;try{System.out.println([动态代理][日志] method.getName()参数Arrays.toString(args));resultmethod.invoke(target,args);System.out.println([动态代理][日志] method.getName()结果result);}catch(Exceptione){e.printStackTrace();System.out.println([动态代理][日志] method.getName() 异常);}finally{System.out.println([动态代理][日志] 方法执行完毕);}returnresult;}};//最后返回代理对象;returnProxy.newProxyInstance(classLoader,interfaces,invocationHandler);}}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 是第三方库所以需要引入对应依赖dependencygroupIdcglib/groupIdartifactIdcglib-nodep/artifactIdversion3.3.0/version/dependency创建方法拦截器实现 CGLIB 的MethodInterceptor接口来定义方法拦截逻辑重写intercept方法第一个参数o是代理对象本身第二个参数method是被调用的目标方法第三个参数objects是目标方法的参数数组第四个参数methodProxy是一个方法代理对象用于调用目标类的原始方法通过methodProxy.invokeSuper(o, objects)可以调用目标类的原始方法并获取返回结果importnet.sf.cglib.proxy.MethodInterceptor;importnet.sf.cglib.proxy.MethodProxy;importjava.lang.reflect.Method;//实现 CGLIB 的MethodInterceptor接口来定义方法拦截逻辑publicclassCglibInterceptorimplementsMethodInterceptor{OverridepublicObjectintercept(Objecto,Methodmethod,Object[]objects,MethodProxymethodProxy)throwsThrowable{System.out.println(代理类在目标方法执行前的操作method.getName());ObjectresultmethodProxy.invokeSuper(o,objects);System.out.println(代理类在目标方法执行后结果result);returnresult;}}//CGLIB 的Enhancer类来创建代理对象importnet.sf.cglib.proxy.Enhancer;publicclassCglibInterceptorMain{publicstaticvoidmain(String[]args){//首先创建一个Enhancer对象EnhancerenhancernewEnhancer();//设置代理类要继承的目标类、设置方法拦截器enhancer.setSuperclass(Calculator.class);enhancer.setCallback(newCglibInterceptor());//最后通过enhancer.create()创建代理对象将其转换为目标类类型Calculatorproxy(Calculator)enhancer.create();//代理类对象调用函数;proxy.add(1,1);proxy.sub(1,1);}}代理模式大致如此是不是so easy轻轻松松的求点赞、收藏⭐

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询