首页 > 其他分享 >代理模式

代理模式

时间:2023-12-27 22:32:19浏览次数:32  
标签:调用 Object 代理 模式 method 方法 public


6 代理模式⭐️

介绍

  • 在代理模式下有两种角色,一种是被代理者,一种是代理(Proxy)。
  • 被代理者需要做一项工作时,不用自己做,而是交给代理做。
  • 使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。
  • 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。
  • 举个例子:你找了小红来帮你问话,小红就可以看作是代理你的代理对象,代理的行为(方法)是问话。

代理模式_代理类

  • 分类
  • 静态代理
  • JDK / CGLIB 动态代理
  • 动态代理
  • 相比于静态代理来说,动态代理更加灵活。我们不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,我们可以直接代理实现类( CGLIB 动态代理机制)。
  • 从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
  • 动态代理在我们日常开发中使用的相对较少,但是在框架中几乎是必用的一门技术。
  • 学会了动态代理之后,对于我们理解和学习各种框架的原理也非常有帮助。
  • 说到动态代理,Spring AOP、RPC 框架应该是两个不得不提的,它们的实现都依赖了动态代理。

静态代理

JDK 动态代理

  • 介绍
  • InvocationHandler 接口和 Proxy 类是核心。
// Proxy 类中使用频率最高的方法是:`newProxyInstance()` ,这个方法主要用来生成一个代理对象。
 
 // loader:类加载器,用于加载代理对象
 // interfaces: 被代理类实现的一些接口
 // h: 实现了 `InvocationHandler` 接口的对象。
 public static Object newProxyInstance(ClassLoader loader, 
                                       Class<?>[] interfaces,
                                       InvocationHandler h)
     throws IllegalArgumentException{
     //......
 }

要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

public interface InvocationHandler {
 
     /**
      * 当你使用代理对象调用方法的时候实际会调用到这个方法
      */
     // proxy:动态生成的代理类
     // method:与代理类对象调用的方法相对应
     // args:当前 method 方法的参数
     public Object invoke(Object proxy, Method method, Object[] args)
         throws Throwable;
 }

也就是说:你通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。

  • JDK 动态代理类使用步骤
  • 定义一个接口及其实现类;
  • 自定义InvocationHandler并重写invoke方法,在invoke方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  • 通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法创建代理对象;
  • 代码示例
  • 定义发送短信的接口
public interface SmsService {
     String send(String message);
 }
  • 实现发送短信的接口
public class SmsServiceImpl implements SmsService {
     public String send(String message) {
         System.out.println("send message:" + message);
         return message;
     }
 }
  • 定义一个 JDK 动态代理类
import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 
 public class DebugInvocationHandler implements InvocationHandler {
     // 代理类中的真实对象
     private final Object target;
 
     public DebugInvocationHandler(Object target) {
         this.target = target;
     }
   
   /**当我们的动态代理对象调用原生方法的时候,最终实际上调用到的是 invoke() 方法,然后 invoke() 方法代替我们去调用了被代理对象的原生方法。
   */
     public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
         //调用方法之前,我们可以添加自己的操作
         System.out.println("before method " + method.getName());
         
         Object result = method.invoke(target, args);
         
         //调用方法之后,我们同样可以添加自己的操作
         System.out.println("after method " + method.getName());
         return result;
     }
 }
  • 获取代理对象的工厂类
public class JdkProxyFactory {
     // 主要通过`Proxy.newProxyInstance()`方法获取某个类的代理对象
     public static Object getProxy(Object target) {
         return Proxy.newProxyInstance(
                 target.getClass().getClassLoader(), // 目标类的类加载
                 target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                 new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
         );
     }
 }
  • 实际使用
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
 smsService.send("java");
  • 运行上述代码之后,控制台打印出:
before method send
 send message:java
 after method send

CGLIB 动态代理

  • 介绍
  • JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类
  • MethodInterceptor 接口和 Enhancer 类是核心。
  • 你需要自定义 MethodInterceptor 并重写 intercept() 方法,intercept() 用于拦截增强被代理类的方法。
  • 你可以通过 Enhancer 类来动态获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor 中的 intercept() 方法。
public interface MethodInterceptor extends Callback{
     // 拦截增强被代理类中的方法
     
     // obj:被代理的对象(需要增强的对象)
   // method:被拦截的方法(需要增强的方法)
   // args:方法入参
   // proxy:用于调用原始方法
     public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                                MethodProxy proxy) throws Throwable;
 }
  • 不需要额外的依赖(不同于 JDK 动态代理)。CGLIB(Code Generation Library) 属于一个开源项目,如果你要使用它的话,需要手动添加相关依赖。
<dependency>
   <groupId>cglib</groupId>
   <artifactId>cglib</artifactId>
   <version>3.3.0</version>
 </dependency>
  • CGLIB 动态代理类使用步骤
  • 定义一个类;
  • 自定义 MethodInterceptor 并重写 intercept() 方法,intercept() 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke() 方法类似;
  • 通过 Enhancer 类的 create()创建代理类;
  • 代码示例
  • 实现一个使用阿里云发送短信的类
package github.javaguide.dynamicProxy.cglibDynamicProxy;
 
 public class AliSmsService {
     public String send(String message) {
         System.out.println("send message:" + message);
         return message;
     }
 }
  • 自定义 MethodInterceptor(方法拦截器)
import net.sf.cglib.proxy.MethodInterceptor;
 import net.sf.cglib.proxy.MethodProxy;

 import java.lang.reflect.Method;

 /**
  * 自定义MethodInterceptor
  */
 public class DebugMethodInterceptor implements MethodInterceptor {
     /**
      * @param o           代理对象(增强的对象)
      * @param method      被拦截的方法(需要增强的方法)
      * @param args        方法入参
      * @param methodProxy 用于调用原始方法
      */
     @Override
     public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
         //调用方法之前,我们可以添加自己的操作
         System.out.println("before method " + method.getName());
         
         Object object = methodProxy.invokeSuper(o, args);
         
         //调用方法之后,我们同样可以添加自己的操作
         System.out.println("after method " + method.getName());
         return object;
     }
 }

对比

  • JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
  • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

标签:调用,Object,代理,模式,method,方法,public
From: https://blog.51cto.com/u_15655475/9004888

相关文章

  • Composite 组合模式简介与 C# 示例【结构型3】【设计模式来了_8】
    Composite组合模式简介与C#示例【结构型3】【设计模式来了_8】 阅读目录〇、简介1、什么是组合设计模式?2、优缺点和适用场景一、简单的代码示例二、根据示例代码看结构三、相关模式回到顶部〇、简介1、什么是组合设计模式?一句话解释:  针对树形结构......
  • Facade 外观模式简介与 C# 示例【结构型5】【设计模式来了_10】
    Facade外观模式简介与C#示例【结构型5】【设计模式来了_10】 阅读目录〇、简介1、什么是外观模式?2、外观模式的优缺点和适用场景一、外观模式的代码实现二、结构三、相关模式回到顶部〇、简介1、什么是外观模式?一句话解释:  将一系列需要一起进行的......
  • Builder 生成器模式简介与 C# 示例【创建型2】【设计模式来了_2】
    Builder生成器模式简介与C#示例【创建型2】【设计模式来了_2】 阅读目录〇、简介1、什么是生成器模式?2、优缺点和使用场景一、简单的示例代码二、生成器模式结构三、在.Net框架中的实际应用四、相关模式回到顶部〇、简介1、什么是生成器模式?一句话......
  • Decorator 装饰者模式简介与 C# 示例【结构型4】【设计模式来了_9】
    Decorator装饰者模式简介与C#示例【结构型4】【设计模式来了_9】 阅读目录〇、简介1、什么是装饰者模式2、优缺点和适用场景一、通过示例代码简单实现二、装饰者模式的结构三、相关模式回到顶部〇、简介1、什么是装饰者模式一句话解释:  通过继承统......
  • 80386保护模式笔记
    目录保护模式简述分段管理机制控制寄存器与系统地址寄存器任务状态段和控制门控制转移任务内无特权级变换的转移,段间转移:任务内不同特权级的变换转移任务切换386中断和异常中断异常中断门或陷阱门的转移转移总结任务切换途径任务内特权集变换途径任务内相同特权级转移的途径操作系......
  • 不会使用 EF Core 的 Code First 模式?来看看这篇文章,手把手地教你
    EFCoreCodeFirst是什么CodeFirst是EntityFrameworkCore(简称EFCore)的一种开发模式,它允许开发人员使用纯粹的代码来定义数据模型,通过它,可以极大地提高开发效率:使用CodeFirst开发模式,你可以专注于定义领域模型和业务逻辑,而无需关注数据库的细节,能够更快地构建应......
  • 清华提出全新代理注意力范式:Softmax注意力与线性注意力的优雅融合
    前言 来自清华大学的研究者提出了一种新的注意力范式——代理注意力(AgentAttention)。本文转载自机器之心仅用于学术分享,若侵权请联系删除欢迎关注公众号CV技术指南,专注于计算机视觉的技术总结、最新技术跟踪、经典论文解读、CV招聘信息。CV各大方向专栏与各个部署框架最......
  • 7、代理池所需相关库安装
    利用代理解决爬虫目标网站封ip的问题,可以使用免费的代理或者付费的代理ip,对于不可用的ip无法及时识别,可以通过搭建ip代理池提高爬虫的工作效率。1、首先所需redis库安装,redis是基于内存的高效的非关系型数据库。github下载地址:版本3.2.100·微软存档/Redis·GitHub的,切后台查......
  • Java 工厂方法设计模式
    需求:Pizza有以下几个方法prepare()准备食材bake()烘焙cut()切割box()装盒现在有2种披萨且和2地区有关,成都地区第一种是CDApplePizza,第二种是CDCheesePizza。北京地区的BJApplePizza和BJApplePizza。PizzaStore有点单系统Order(city,pizzaType)根据pizzatype去制作对......
  • Java 简单工厂模式
    需求:Pizza有以下几个方法prepare()准备食材bake()烘焙cut()切割box()装盒现在有两种披萨,第一种是ApplePizza,第二种是CheesePizza。PizzaStore有点单系统Order(pizzaType)根据pizzatype去制作对应的Pizza。Pizza的抽象类publicabstractclassPizza{Strin......