首页IT科技动态代理实现方式和区别(动态代理与责任链模式)

动态代理实现方式和区别(动态代理与责任链模式)

时间2025-07-07 14:21:42分类IT科技浏览4959
导读: 动态代理和责任链设计模式适用范围广,在Spring和MyBatis有着重要的应用,比如SpringAOP、Mybatis的插件技术,想要搞懂当中的技术原理必须掌握上面两个设计模式。...

  动态代理和责任链设计模式适用范围广              ,在Spring和MyBatis有着重要的应用                     ,比如SpringAOP              、Mybatis的插件技术       ,想要搞懂当中的技术原理必须掌握上面两个设计模式              。

代理模式可以理解为您要操作一个对象              ,但是要经过这个对象的“代理              ”对象去操作                     。就好似你在一家软件公司做开发                     ,客户发现程序有Bug       ,会找到商务对接人说       ,最后商务的同事再找到你去解决问题       。“商务                     ”是代理对象                     ,“你       ”是真实对象              。代理模式分为静态代理和动态代理              ,其作用是可以在真实对象访问之前或者之后加入自定义的逻辑       ,又或者根据自定义规则来控制是否使用真实对象                     。

静态代理是真实对象与代理对象(Proxy)实现相同的接口                     ,代理对象包含真实对象的引用              ,客户端通过代理对象去访问真实对象,代理对象可以在真实对象访问之前或之后执行其他操作       。假设要你设计一个对外开放的商品库存信息查询接口                     ,并且要限制调用方在一天时间内的调用次数       。用代理模式代理库存接口                     ,首先在库存查询前检查用户是否有权限访问,然后在查询后要记录用户查询日志              ,以便根据查询次数                     ,判断调用上限                     。

动态代理是在程序运行时       ,通过反射机制创建代理对象              ,实现动态代理方法              。动态代理相比于静态代理的好处                     ,是代理对象不用实现真实对象的接口       ,这样能代理更多方法       。因为静态代理是一个接口对应一个类型       ,如果接口添加新方法                     ,则所有代理类都要实现此方法              ,所以动态代理脱离了接口实现       ,一个代理类就能代理更多方法                     。一些公共代码逻辑也就可以在多个代理方法里复用                     ,例如:数据库事务开启                     、提交       、回滚              ,这些公共代码都分别是在真实方法调用的前后出现,而动态代理会帮我们把功能代码织入到方法里              。在Java中最常用动态代理有两种                     ,一种是JDK动态代理                     ,这是JDK自带的功能;另一种是CGLIB,由第三方提供的一个技术              ,Spring是用了JDK代理和CGLIB两种                     ,两者的区别是       ,JDK代理要提供接口作为于代理参数才能使用              ,而CGLIB不需要提供接口                     ,只要一个非抽象类就能代理       ,适用于一些不能提供接口的场景。

1. JDK动态代理

  (1)定义真实对象接口       ,因为JDK动态代理要借助接口才能代理对象

  (2)创建代理类                     ,实现java.lang.reflect.InvocationHandler接口

public class JdkProxyExample implements InvocationHandler { // 真实对象 private Object target = null; public Object bind(Object target) { this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("代理前"); System.out.println("调用真实对象"); Object r = method.invoke(target, args); System.out.println("代理后"); return r; } }

  Proxy.newProxyInstance方法的作用是创建代理对象              ,并建立代理对象与真实对象的关系                     。包含3个参数

第1个是类加载器       ,用于把Java字节码转换成Class类实例对象                     ,这里用了target对象所属的类加载器

第2个是生成的动态代理对象需要挂载到哪些接口下              ,上面是取了真实对象的接口

第3个是实现InvocationHandler接口的代理类,this表示当前对象

  InvocationHandler接口的invoke方法实现代理逻辑                     ,invoke其中参数含义如下

proxy:代理对象                     ,就是bind方法生成的对象

method:当前调用方法,method.invoke(target, args)调用真实对象方法

args:当前调用方法参数

  (3)测试JDK动态代理

public void testJdk() { JdkProxyExample jdkProxy = new JdkProxyExample(); SayService proxy = (SayService) jdkProxy.bind(new SayServiceImpl()); proxy.sayHello(); } /* 代理前 调用真实对象 Hello Friend 代理后 */

2. CGLIB动态代理

JDK动态代理要提供接口才能代理              ,但在一些不能提供接口的场景下                     ,CGLIB动态代理技术不需要提供接口       ,只要一个非抽象类就能动态代理                     。新建类实现MethodInterceptor接口(spring框架的cglib包)              ,使用Enhacer创建代理对象。代码实现如下:

import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxyExample implements MethodInterceptor { public Object bind(Class classz) { Enhancer enhancer = new Enhancer(); enhancer.setCallback(this); enhancer.setSuperclass(classz); return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("调用真实对象前"); Object result = methodProxy.invokeSuper(proxy, args); System.out.println("调用真实对象后"); return result; } } @Test public void testCglib() { CglibProxyExample cglibProxy = new CglibProxyExample(); TestService proxy = (TestService)cglibProxy.bind(TestService.class); proxy.sayHello(); } /* 调用真实对象前 Hi hello 调用真实对象后 */

3. 拦截器

由于动态代理一般不好理解                     ,通常会设计一个拦截器接口提供给开发者使用       ,这样只需要知道拦截器接口方法              、含义和作用即可       ,无须知道动态代理是怎么实现              。拦截器接口设计如下:

public interface Interceptor { boolean before(Object target); Object around(Object target, Method method, Object[] args); void after(Object target); void afterThrowing(Object target); void afterReturning(Object target); }

  假定拦截器使用规则是:在调用真实方法前                     ,先访问before方法              ,如果返回true       ,执行真实对象方法                     ,否则执行around方法代替真实方法的调用                     。after方法在真实方法或around调用后执行              ,如果真实方法或around调用异常,执行afterThrowing方法                     ,否则执行afterReturning方法       。

定义了以上规则                     ,开发者只要知道拦截器怎样使用,不需要知道动态代理怎么实现              。

下面代码演示如何使用上面定义的拦截器

public class SayServiceInterceptor implements Interceptor { @Override public boolean before(Object target) { System.out.println("代理前执行before方法"); // 不执行真实对象的方法 return false; } @Override public Object around(Object target, Method method, Object[] args) { System.out.println("真实对象方法被替换              ,执行around"); return null; } @Override public void after(Object target) { System.out.println("代理后执行after方法"); } @Override public void afterThrowing(Object target) { System.out.println("真实对象方法调用异常                     ,执行afterThrowing方法"); } @Override public void afterReturning(Object target) { System.out.println("最后执行afterReturning方法"); } } public void testJdk() { SayService say = new SayServiceImpl(); SayServiceInterceptor interceptor = new SayServiceInterceptor(); SayService proxy = (SayService) JdkProxyExample.bind(say, interceptor); proxy.sayHello(); } /** 代理前执行before方法 真实对象方法被替换       ,执行around 代理后执行after方法 最后执行afterReturning方法 */

上面规则用动态代理实现代码如下:

public class JdkProxyExample implements InvocationHandler { // 真实对象 private Object target = null; // 拦截器 private Interceptor interceptor; public JdkProxyExample(Object target, Interceptor interceptor) { this.target = target; this.interceptor = interceptor; } public static Object bind(Object target, Interceptor interceptor) { return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new JdkProxyExample(target, interceptor)); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(interceptor == null) { return method.invoke(target, args); } Object result = null; boolean exceptionFlag = false; try { if (interceptor.before(target)) { result = method.invoke(target, args); }else { result = interceptor.around(target, method, args); } }catch (Exception e) { exceptionFlag = true; } finally { interceptor.after(target); } if(exceptionFlag) { interceptor.afterThrowing(target); }else { interceptor.afterReturning(target); } return result; } }

设计拦截器能简化了动态代理的使用方法              ,使程序更简单                     。在实际使用场景中拦截器实现后                     ,要在开发者的程序进行xml配置       ,或者在实现类添加注解标识等方式       ,来查找到拦截器实现类                     ,然后反射创建并加载到程序里       。SpringAOP也是通过@Aspect注解创建切面(拦截器)              ,@execution定义连接点拦截方法       。

4. 责任链模式

设计拦截器去代替动态代理       ,然后将拦截器的接口提供给开发者用                     ,从而简化开发者的开发难度              ,但是拦截器可能会有多个,举个例子                     ,您要请假一周                     ,然后在OA上提交请假申请单,要经过项目经理                     、部门经理       、人事等多个角色的审批              ,每个角色都会对申请单拦截       、修改                     、审批等                     。如果把请假申请单看做一个对象                     ,则它会经过三个拦截器的拦截处理              。当一个对象在一条链上被多个拦截器拦截处理时       ,我们把这样的设计模式称为责任链模式       。前一个拦截器的返回结果会作用于后一个拦截器              ,代码上的实现是用后一个拦截器去代理了前一个拦截器的方法                     ,以此类推       ,层层代理                     。最终结果如下图:

 代码实现如下:

@Test public void testInterceptor() { SayService target = new SayServiceImpl(); SayService proxy1 = (SayService)JdkProxyExample.bind(target, new SayServiceInterceptor("proxy1")); SayService proxy2 = (SayService)JdkProxyExample.bind(proxy1, new SayServiceInterceptor("proxy2")); SayService proxy3 = (SayService)JdkProxyExample.bind(proxy2, new SayServiceInterceptor("proxy3")); proxy1.sayHello(); } /** proxy3:代理前执行before方法 proxy2:代理前执行before方法 proxy1:代理前执行before方法 Hello Friends proxy1:代理后执行after方法 proxy2:代理后执行after方法 proxy3:代理后执行after方法 */

  before方法执行顺序是从最后一个拦截器到第一个拦截器       ,而after方法是从第一个拦截器到最后一个              。责任链模式的优点在于我们可以在传递链中加上新的拦截器                     ,增加拦截逻辑              ,但缺点是每增加一层代理反射       ,程序性能越差。

声明:本站所有文章                     ,如无特殊说明或标注              ,均为本站原创发布                     。任何个人或组织,在未征得本站同意时                     ,禁止复制              、盗用       、采集                     、发布本站内容到任何网站              、书籍等各类媒体平台                     。如若本站内容侵犯了原著者的合法权益                     ,可联系我们进行处理。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
win10通知界面怎么关闭(win10中的通知栏怎样添加或删除图标通知)