【操持模式-结构型】代理模式

打印 上一主题 下一主题

主题 995|帖子 995|积分 2985

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

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

x
一、什么是代理模式

        在港片中,经常能看到一些酷炫的大哥被警察抓了,警察会试图从他们口中套出一些关键信息。但这些大哥们通常会非常冷静地回应:“我有权保持沉默,我要找我的律师。” 
        这个律师就像是大哥的“法律盾牌”,全权处理全部法律事务。律师的脚色不但仅是代理大哥发言,更是在法律的战场上为大哥披荆斩棘。详细来说,律师会做以下几件事情:

  • 准备法律文件:律师会经心准备各种法律文件,确保每一份文件都无懈可击,为大哥的辩护打下坚固的底子。
  • 与检察官沟通:律师会与检察官举行多轮沟通,夺取对大哥最有利的条件,乃至大概会在法庭上展开一场猛烈的辩论。
  • 法庭辩护:在法庭上,律师会代表大哥发言,用专业的法律知识和丰富的经验,为大哥举行有力的辩护,确保大哥的权益不受侵占。
  • 提供法律发起:律师还会向大哥提供专业的法律发起,资助他了解自己的权利和义务,让他在法律的框架内做出最明智的决定。
        这个过程就像是一场经心策划的战役,律师作为代理,不但代理了大哥的发言,还在操纵执行前后添加了诸多增强处理,确保大哥在法律的保护下安然无恙。这种场景在港片中家常便饭,不但展示了法律的严肃性,还增添了一丝戏剧性和紧张感,让人看得津津有味。
       港片中的大哥,就是运用代理模式的行家能手。下面来说代理模式,代理模式是一种结构型的操持模式,代理对象(律师)具备真实对象(犯事大哥)的功能,并代替真实对象完成相应操纵(律师代表大哥讲话),同时可以在操纵执行前后举行增强处理(准备法律文件、与检察官沟通等)。
二、为什么用代理模式

        通过一个形象的例子我们知道了代理模式,通过这个例子我们可以聊一下为什么要用代理模式(请律师):


  • 权限控制(保护目标对象):保护客户的隐私和权益,制止不当操纵。律师作为代理,可以控制犯罪猜疑人与外界的交流,确保全部信息的传递都在法律框架内举行。比方,律师可以决定哪些信息可以透露给检察官,哪些信息必要保密。
  • 优化性能(提高效率):代理模式可以实现资源的延迟加载和缓存,提高资源利用效率,减少不必要的资源斲丧。律师在必要时才会准备详细的法律文件,而不是一开始就举行大量的准备工作。比方,只有在确定案件进入法庭审理阶段时,律师才会准备详细的辩护词。
  • 增加功能(减轻目标对象的负担):代理模式可以在代理对象中添加额外的功能,而不必要修改原始对象的代码。
  • 简化客户端代码:客户端可以更专注于核心业务,而不必处理复杂的细节。犯罪猜疑人只必要与律师沟通,而不必要直接处理复杂的法律事务。
三、代理模式示例

代理对象分为2类分别是静态代理和动态代理
3.1 静态代理

        静态代理必要我们自己去实当代理模式。下面以请律师为demo实现一个静态代理的示例。从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是认真实类的方法越来越多的时间,这样构建的代理类的代码量优劣常大的,所以就引进动态代理。
1、定义法律援助接口
  1. public interface LegalService {
  2.     void representClient();
  3. }
复制代码
2、目标对象(大哥) 
  1. public class Suspect implements LegalService {
  2.     @Override
  3.     public void representClient() {
  4.         System.out.println("我是大哥,我需要律师为我进行辩护.");
  5.     }
  6. }
复制代码
3、实当代理(律师)
  1. public class LawyerProxy implements LegalService {
  2.     private Suspect suspect;
  3.     public LawyerProxy(Suspect suspect) {
  4.         this.suspect = suspect;
  5.     }
  6.     @Override
  7.     public void representClient() {
  8.         prepareLegalDocuments();
  9.         communicateWithProsecutor();
  10.         suspect.representClient();
  11.         provideLegalAdvice();
  12.     }
  13.     private void prepareLegalDocuments() {
  14.         System.out.println("准备法律文件.");
  15.     }
  16.     private void communicateWithProsecutor() {
  17.         System.out.println("与检察官进行战略沟通,协商有利条件.");
  18.     }
  19.     private void provideLegalAdvice() {
  20.         System.out.println("为嫌疑人提供专业法律建议,确保其权利得到保护.");
  21.     }
  22. }
复制代码
4、 场景
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         Suspect suspect = new Suspect();
  4.         LegalService lawyer = new LawyerProxy(suspect);
  5.         lawyer.representClient();
  6.     }
  7. }
  8. //输出场景内容
  9. 准备法律文件.
  10. 与检察官进行战略沟通,协商有利条件.
  11. 我是大哥,我需要律师为我进行辩护.
  12. 为嫌疑人提供专业法律建议,确保其权利得到保护.
复制代码
3.2 动态代理

        动态代理是开源工具提供给我们实现好的工具可以直接利用(动态代理允许利用一种方法的单个类(代理类)为具有恣意数量方法的恣意类(真实类)的多个方法调用提供服务。其实运用的是反射机制)。目前来说接触到的动态代理主要有2种分别对应的是jdk实现的动态代理 和 cglib动态代理。
3.2.1 jdk动态代理(接口代理)

3.2.1.1 jdk相干类和核心方法简单描述

        jdk动态代理主要利用的是 java.lang.reflect包。
        核心类:InvocationHandler接口    Proxy类
        核心方法:invoke newProxyInstance
  1. public Object invoke(Object proxy,Method method,Object[] args) throws Throwable
  2. public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
复制代码
 3.2.1.2 jdk动态代理代码示例  

法律援助接口和目标对象(大哥)不用变利用静态代理的代码,其他举行修改。
1、实现InvocationHandler ,作为代理处理器
InvocationHandler,这个处理器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. public class LawyerProxyHandler implements InvocationHandler {
  4.     private final LegalService realLawyer;
  5.     public LawyerProxyHandler(LegalService realLawyer) {
  6.         this.realLawyer = realLawyer;
  7.     }
  8.     @Override
  9.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  10.         // 在调用真实方法之前执行的逻辑
  11.         preHandle(args);
  12.         // 调用真实方法
  13.         Object result = method.invoke(realLawyer, args);
  14.         // 在调用真实方法之后执行的逻辑
  15.         postHandle(args);
  16.         return result;
  17.     }
  18.     private void preHandle(Object[] args) {
  19.         System.out.println("律师正在准备法律文件和证据.");
  20.     }
  21.     private void postHandle(Object[] args) {
  22.         System.out.println("律师已完成对委托人的代理.");
  23.     }
  24. }
复制代码
2、创建详细代理对象(律师)
  1. import java.lang.reflect.Proxy;
  2. public class LegalServiceProxy {
  3.     public static LegalService getProxyInstance(LegalService realLawyer) {
  4.         return (LegalService) Proxy.newProxyInstance(
  5.             realLawyer.getClass().getClassLoader(), // 目标类的类加载器
  6.             new Class<?>[]{LegalService.class}, // 目标类实现的接口
  7.             new LawyerProxyHandler(realLawyer) // 代理处理器
  8.         );
  9.     }
  10. }
复制代码
3、利用场景
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         // 创建真实对象
  4.         LegalService dg = new RealLawyer();
  5.         // 创建代理对象
  6.         LegalService lawyerProxy = LegalServiceProxy.getProxyInstance(dg);
  7.         // 通过代理对象调用方法
  8.         lawyerProxy.representClient();
  9.     }
  10. }
  11. //输出
  12. 律师正在准备法律文件和证据.
  13. 我是大哥,我需要律师为我进行辩护.
  14. 律师已完成对委托人的代理.
复制代码
3.2.2 cglib动态代理(继承代理)

        从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时间就必要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码举行修改和动态生成。CGLIB通过继承方式实当代理。
1、大哥类要改一下,因为没有接口
  1. public class Suspect{
  2.     @Override
  3.     public void representClient() {
  4.         System.out.println("我是大哥,我需要律师为我进行辩护.");
  5.     }
  6. }
复制代码
2、 实现MethodInterceptor,作为代理处理器
实现MethodInterceptor接口,这个拦截器将拦截对真实对象的调用,并在调用前后添加额外的逻辑。
  1. import net.sf.cglib.proxy.MethodInterceptor;
  2. import net.sf.cglib.proxy.MethodProxy;
  3. import java.lang.reflect.Method;
  4. public class LawyerProxyInterceptor implements MethodInterceptor {
  5.     private final Suspect suspect;
  6.     public LawyerProxyInterceptor(Suspect suspect) {
  7.         this.suspect = suspect;
  8.     }
  9.     @Override
  10.     public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
  11.         // 在调用真实方法之前执行的逻辑
  12.         preHandle(args);
  13.         // 调用真实方法
  14.         Object result = proxy.invokeSuper(obj, args);
  15.         // 在调用真实方法之后执行的逻辑
  16.         postHandle(args);
  17.         return result;
  18.     }
  19.      private void preHandle(Object[] args) {
  20.         System.out.println("律师正在准备法律文件和证据.");
  21.     }
  22.     private void postHandle(Object[] args) {
  23.         System.out.println("律师已完成对委托人的代理.");
  24.     }
  25. }
复制代码
3、 创建详细代理对象(律师)
        利用CGLIB的Enhancer类创建代理对象。这个代理对象将代理真实对象,并在调用方法时通过MethodInterceptor举行拦截。
  1. import net.sf.cglib.proxy.Enhancer;
  2. public class LegalServiceProxy {
  3.     public static RealLawyer getProxyInstance(Suspect suspect) {
  4.         Enhancer enhancer = new Enhancer();
  5.         enhancer.setSuperclass(suspect.getClass()); // 设置目标类
  6.         enhancer.setCallback(new LawyerProxyInterceptor(suspect)); // 设置回调
  7.         return (Suspect) enhancer.create(); // 创建代理对象
  8.     }
  9. }
复制代码
4、利用场景 
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         // 创建真实对象
  4.         Suspect suspect = new Suspect();
  5.         // 创建代理对象
  6.         Suspect lawyerProxy = LegalServiceProxy.getProxyInstance(suspect);
  7.         // 通过代理对象调用方法
  8.         lawyerProxy.representClient();
  9.     }
  10. }
  11. //输出
  12. 律师正在准备法律文件和证据.
  13. 我是大哥,我需要律师为我进行辩护.
  14. 律师已完成对委托人的代理.
复制代码
3.2.3 动态代理总结

JDK动态代理和CGLIB动态代理是Java中实现动态代理的两种常用机制,虽然它们都可以为目标对象创建代理对象并拦截方法调用,但它们的工作原理和利用场景有所不同。以下是它们的区别:
1. 基于接口 vs 基于类继承



  • JDK动态代理

    • 基于接口:JDK动态代理要求被代理的类必须实现一个或多个接口。假如类没有实现任何接口,JDK动态代理将无法工作。
    • 长处:实现简单,利用Java内置API、无需依赖第三方库。
    • 缺点:只能代理接口,不能代理平凡类。方法调用时利用反射,性能相对较低。

  • CGLIB动态代理

    • 基于类集成:CGLIB动态代理通过生成目标类的子类来实当代理。它不要求目标类必须实现接口,因此它实用于没有实现接口的类。
    • 长处:可以代理没有接口的类。方法调用性能较高,制止了反射调用。
    • 缺点:创建代理类时必要举行字节码操纵,性能开销较大。必要依赖cglib和ASM库。

2. 实现机制



  • JDK动态代理

    • 利用反射机制,通过 java.lang.reflect.Proxy 类和 InvocationHandler 接口来实当代理。代理对象仅代理接口中的方法。
    • 当调用代理对象的方法时,代理类会拦截方法调用,并通过 InvocationHandler.invoke() 方法执行额外的逻辑。

  • CGLIB动态代理

    • 基于字节码操纵,利用 CGLIB(Code Generation Library)生成目标类的子类并重写目标类的方法来实当代理。通过继承方式拦截全部非 final 方法的调用。
    • CGLIB 利用的是 ASM 字节码生成框架,生成的是字节码级别的代理类,因此性能相对较好,但生成代理类的开销比JDK动态代理略大。

3. 性能差异



  • JDK动态代理

    • 对于实现了接口的类来说,JDK动态代理在创建代理对象时开销较小,因为它仅依赖反射机制来处理接口方法的调用。
    • 对于频繁调用代理方法的场景,JDK动态代理大概比CGLIB略慢,因为每次调用都涉及反射。

  • CGLIB动态代理

    • 由于CGLIB是通过字节码生成来创建代理类,生成代理类的开销比JDK动态代理高一些,尤其是在代理类较多的情况下。
    • 但CGLIB代理的实际方法调用性能更高,因为它通过字节码操纵,减少了反射调用的开销。

4. 利用场景



  • JDK动态代理

    • 实用于接口驱动的编程,假如目标类实现了接口,那么利用JDK动态代理是首选方式。
    • 适合在不必要对类举行直接代理的场景,通常在应用中,业务逻辑往往是通过接口定义的,因此JDK代理在实际项目中更常用。

  • CGLIB动态代理

    • 实用于没有实现接口的类,比方一些现有类或者第三方库的类没有提供接口的情况下,可以利用CGLIB动态代理。
    • 实用于对类举行代理时,但必要留意类不能是 final,否则CGLIB无法生成代理子类。

5. Spring AOP中的利用



  • JDK动态代理

    • 在Spring AOP中,假如目标对象实现了接口,Spring默认利用JDK动态代理。这是因为Spring AOP的核心头脑是基于接口的面向切面编程(Aspect-Oriented Programming)。

  • CGLIB动态代理

    • 假如目标对象没有实现任何接口,Spring AOP会自动利用CGLIB动态代理。在Spring设置中,你也可以强制利用CGLIB代理(通过设置 proxyTargetClass=true)。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

忿忿的泥巴坨

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表