首页 > 其他分享 >【设计模式】代理模式详解

【设计模式】代理模式详解

时间:2024-07-29 10:58:26浏览次数:16  
标签:设计模式 对象 代理 接口 详解 动态 方法 public

1.简介

代理模式是常用的Java设计模式,该模式的特点是代理类与委托类共享相同的接口。代理类主要负责预处理消息、过滤消息、将消息转发给委托类,并在事后处理消息等。代理类与委托类之间通常存在关联关系,一个代理类对象与一个委托类对象关联。代理类对象本身不真正实现服务,而是通过调用委托类对象的相关方法来提供特定的服务。

代理模式主要包括以下角色:

  • 抽象主题(Subject):定义代理类和委托类(RealSubject)的共同接口。这个接口规定了代理类和委托类必须实现的方法,代理类可以通过这个接口来调用委托类的方法。
  • 真实主题(RealSubject):实现抽象主题,定义委托类的操作。它包含了实际的业务逻辑,是客户端实际需要调用的对象。
  • 代理类(Proxy):实现抽象主题,持有对委托类的引用,并在其方法被调用时进行控制。代理类在调用委托类的方法前后可以添加一些额外的功能,如日志记录、权限控制、事务处理等。

2.静态代理

**静态代理:**在编译时期确定代理类和目标类的关系,代理类和目标类都要实现同一个接口。

定义一个简单的例子:假如一个租客需要租房子,他可以直接通过**房东(委托类)去租房,也可以经过中介(代理类)**去租房。房东(realsubject)和中介(proxy)都需要实现subject接口实现房子出租。

  1. 确定接口具体行为

首先创建一个Person接口。这个接口是房东和中介的共同接口,租房行为可以被中介代理。

public interface Person {
    // 出租房子
    void hire();
}
  1. 编写委托类业务逻辑

创建一个委托类,实现subject接口,并编写业务逻辑

public class Landlord implements Person{
	// 房东直售
    @Override
    public void hire() {
        System.out.println("出租,收款1000元");
    }
}
  1. 代理类增强方法

创建一个代理类,同样实现subject接口,对委托类的方法进行增强

public class Agency implements Person{

    private final Landlord landlord;

    public Agency(Landlord landlord) {
        this.landlord = landlord;
    }
	// 中介出租,额外收取费用
    @Override
    public void hire() {
        System.out.println("开始办理租房手续");
        landlord.hire();
        System.out.println("额外收取中介费200元");
    }
}
  1. 测试类使用代理对象
public class Main {
    public static void main(String[] args) {
        // 获取代理对象
        Landlord landlord = new Landlord();
        Agency agency = new Agency(landlord);
        // 使用代理方法
        agency.hire();
    }
}

输出结果如下,可以发现对方法进行了增强

3.动态代理

**动态代理:**在程序运行时动态生成代理类(subject的实现类)。

相比于静态代理, 动态代理的优势在于其较高的灵活性和代码复用性。同一个动态代理处理器可以代理多个目标对象,而静态代理则需要创建大量的代理类。

在Java中,可以通过JDK和CGLIB实现动态代理。

3.1.JDK动态代理

实现原理

JDK动态代理:在java的java.lang.reflect包下提供了Proxy类和InvocationHandler接口,利用这两个类和接口,可以在运行时动态生成指定接口的实现类

Proxy类就是用来创建一个代理对象的类,在JDK动态代理中我们需要使用其newProxyInstance方法。

public static Object newProxyInstance(
    ClassLoader loader, 
    Class<?>[] interfaces, 
    InvocationHandler h);

这个方法的作用就是创建一个代理类对象,它接收以下三个参数:

  • loader:一个ClassLoader对象,指定哪个ClassLoader将加载生成的代理类。
  • interfaces:一个Interface对象数组,定义代理对象实现的一组接口,代理类可以调用这些接口中声明的所有方法。
  • h:一个InvocationHandler对象,指定代理对象的方法调用将关联到哪个InvocationHandler对象,由它处理实际的方法调用。

InvocationHandler接口提供了一个invoke方法,当代理对象调用方法时,invoke方法会被调用。通过实现这个接口,可以在方法调用前后添加自定义逻辑。

/**
    * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
    * method:我们所要调用某个对象真实的方法的Method对象
    * args:指代代理对象方法传递的参数
    */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
代码实现
  1. 确定接口具体行为

这里我们设计两个接口

public interface HouseServiceA {
    // 出租房子
    void hire();
}
public interface HouseServiceB {
    // 转租房子
    void sublet();
}
  1. 编写委托类业务逻辑

委托类实现这两个接口,并且定义具体的业务逻辑

public class HouseServiceImpl implements HouseServiceA, HouseServiceB {

    @Override
    public void hire() {
        System.out.println("出租房子");
    }

    @Override
    public void sublet() {
        System.out.println("转租房子");
    }
}
  1. 编写代理工厂代码

代理工厂负责在运行时动态生成代理类,需要实现InvocationHandler接口重写invoke方法来做方法增强,使用Proxy类创建代理对象。

/**
 * JDK动态代理实现InvocationHandler接口
 */
public class HouseFactory implements InvocationHandler {
    private Object target;

    //定义获取代理对象的方法(将目标对象传入进行代理)
    public Object getJDKProxy(Object target){
        //为目标对象target赋值
        this.target = target;
        //JDK动态代理只能针对实现了接口的类进行代理,因此需要传递接口的class
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                new Class<?>[]{HouseServiceA.class, HouseServiceB.class},
                this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK动态代理开始");
        // 调用invoke方法,result存储该方法的返回值
        Object result = method.invoke(target, args);
        System.out.println("JDK动态代理结束");
        return result;
    }
}
  1. 测试类使用代理对象

在实际使用时,只需要将工厂类生成的代理对象转为需要的代理类,即可实现同时代理多个接口的方法。

public class Main {
    public static void main(String[] args) {
        // 创建代理类工厂
        HouseFactory factory = new HouseFactory();
        // 动态生成接口A的代理类
        HouseServiceA houseProxyA = (HouseServiceA) factory.getJDKProxy(new HouseServiceImpl());
        houseProxyA.hire();
        System.out.println("=====================");
        // 动态生成接口B的代理类
        HouseServiceB houseProxyB = (HouseServiceB) factory.getJDKProxy(new HouseServiceImpl());
        houseProxyB.sublet();
    }
}

返回结果:

3.2.CGLIB动态代理

实现原理

CGLIB动态代理:依赖于ASM下的Enhancer类和MethodInterceptor接口,可以在运行时动态生成目标类的子类

Enhancer类是用来创建代理对象的类。在CGLIB动态代理中,我们需要使用其create方法。

public class Enhancer {
    public Object create();
    // 其他方法
}

这个方法的作用是创建一个代理类对象,通常还需要设置以下几个属性:

  • setSuperclass:设置被代理的目标类,CGLIB通过生成目标类的子类来实现代理。
  • setCallback:设置回调接口,用于处理代理对象的方法调用。

MethodInterceptor接口提供了一个intercept方法,当代理对象调用方法时,intercept方法会被调用。通过实现这个接口,可以在方法调用前后添加自定义逻辑。

/**
    * obj: 代理对象
    * method: 被代理的方法
    * args: 方法的参数
    * proxy: 用于调用父类方法的代理
    */
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;

与JDK动态代理不同,CGLIB代理不需要目标类实现接口。CGLIB通过生成目标类的子类并重写方法来实现代理,因此它可以代理没有实现接口的类。

代码实现
  1. 首先导入依赖
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
  1. 编写委托类业务逻辑(无需实现接口)
public class HouseServiceImpl {

    public void hire() {
        System.out.println("出租房子");
    }

    public void sublet() {
        System.out.println("转租房子");
    }
}
  1. 测试类使用代理对象
public class Main {
    public static void main(String[] args) {
        HouseFactory factory = new HouseFactory();
        HouseServiceImpl cglibProxy = (HouseServiceImpl) factory.getCglibProxy(new HouseServiceImpl());
        cglibProxy.hire();
        System.out.println("=====================");
        cglibProxy.sublet();
    }
}

返回结果:

4.总结

静态代理

实现方式:

  • 由程序员显式编写代理类。代理类在编译期确定,编译前就存在代理类的字节码文件。
  • 需要实现与目标对象相同的接口,且在代理类中显式调用目标对象的方法。

优点:

  • 结构简单,容易理解。

缺点:

  • 每增加一个接口,都需要编写对应的代理类,代码量大,维护成本高。静态代理类在编译期生成,灵活性差。

JDK动态代理

实现方式:

  • 使用java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
  • 代理类在运行时动态生成,不需要显式编写代理类。

优点:

  • 代理类在运行时生成,增加了代码的灵活性和可维护性。

缺点:

  • 只能代理实现了接口的类,不能代理没有实现接口的类。

CGLIB动态代理

实现方式:

  • 使用CGLIB(Code Generation Library),依赖ASM字节码生成框架。
  • 代理类在运行时动态生成,不需要显式编写代理类。

优点:

  • 不要求目标类实现接口,可以代理普通的类。
  • 性能通常比JDK动态代理更高,尤其在代理大量方法调用时更为显著。

缺点:

  • 不能代理final类和final方法。

适用场景:

  • 静态代理:需要手动编写代理类,适用于简单的场景,但不够灵活,维护成本高。

  • JDK动态代理:适用于实现了接口的类,代理类在运行时生成,灵活性高,但只能代理接口。

理实现了接口的类,不能代理没有实现接口的类。

CGLIB动态代理

实现方式:

  • 使用CGLIB(Code Generation Library),依赖ASM字节码生成框架。
  • 代理类在运行时动态生成,不需要显式编写代理类。

优点:

  • 不要求目标类实现接口,可以代理普通的类。
  • 性能通常比JDK动态代理更高,尤其在代理大量方法调用时更为显著。

缺点:

  • 不能代理final类和final方法。

适用场景:

  • 静态代理:需要手动编写代理类,适用于简单的场景,但不够灵活,维护成本高。

  • JDK动态代理:适用于实现了接口的类,代理类在运行时生成,灵活性高,但只能代理接口。

  • CGLIB动态代理:适用于没有实现接口的类,性能优于JDK动态代理,但不能代理final类和final方法,且使用复杂度稍高。

标签:设计模式,对象,代理,接口,详解,动态,方法,public
From: https://blog.csdn.net/k123456kah/article/details/140765668

相关文章

  • Redis中pipeline(管道)详解
    redis管道pipeline举个例子:小卖铺免费让你拿50瓶饮料,你是一次拿一瓶拿回家,还是打包一次或者多次拿回家?概念Redis管道(pipelining)是一种在客户端向服务端发送多个请求而不等待响应的技术。它可以显著提高Redis应用程序的性能。管道的主要思想是客户端向服务端发送多个请求......
  • (BS ISO 11898-1:2015)CAN_FD 总线协议详解5- MAC子层描述4
    5.5帧编码帧中的比特流应按照不归零(NRZ,Non-Return-to-Zero)方法进行编码。这意味着在整个比特时间内生成的比特电平是恒定不变的。为了限制可用于同步的最大边沿(即信号波形的上升沿或下降沿)间距,帧的不同部分如起始边界(SOF,StartofFrame)、仲裁字段、控制字段、数据字段以......
  • VO、DTO、Entity:Java 应用中的数据对象详解
    在Java应用程序中,特别是在基于微服务架构的应用中,数据对象(DataObjects)扮演着非常重要的角色。它们不仅有助于组织和传输数据,还能确保应用程序各部分之间的解耦。本文将深入探讨VO(ViewObject)、DTO(DataTransferObject)和Entity之间的区别,并讨论它们在实际项目中的应......
  • proc/meminfo详解
    [root@iZ2ze7ukvpkonzby0h3wbfZ~]#cat/proc/meminfoMemTotal:16265476kB//可用的总内存MemFree:212936kB//完全未用到的物理内存LowFree+HighFreeMemAvailable:1968228kB//MemAvailable≈MemFree+Buffers+CachedBuffers:......
  • 电动垂起固定翼+倾斜摄影+激光数据:实时三维城市采集技术详解
    电动垂起固定翼无人机结合倾斜摄影与激光数据技术,为实时三维城市采集提供了高效、精确的解决方案。以下是对这一技术的详细解析:一、电动垂起固定翼无人机1.无人机特点垂直起降能力:电动垂起固定翼无人机结合了固定翼和多旋翼的优点,能够在有限的起降空间内实现垂直起降,从而适......
  • 无人机运营合格证及AOPA飞行执照技术详解
    随着无人机技术的飞速发展,其在航拍、农业、物流、环境监测、应急救援等领域的应用日益广泛,无人机运营与飞行人员的规范化管理显得尤为重要。本文将围绕无人机运营合格证(简称“合格证”)及中国航空器拥有者及驾驶员协会(AOPA)颁发的飞行执照进行技术详解,涵盖其定义、技术培训要点、......
  • ossutil命令详解
    ossutil是阿里云提供的一款命令行工具,用于管理和操作阿里云对象存储服务(OSS)。以下是ossutil常用的一些命令和功能:安装和配置安装ossutil:可以从阿里云官网下载对应平台的ossutil安装包https://help.aliyun.com/zh/oss/developer-reference/install-ossutil?spm=a2......
  • Vuex和Pinia详解
    Vuex基础知识Vuex是一个专为Vue.js应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex的核心构成要素包括:State(状态):存储应用的状态。Getter(获取器):从状态中派生出状态。Mutation(突变):同步地更......
  • 【Python学习手册(第四版)】学习笔记06-Python动态类型-赋值模型详解
    个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。主要介绍Python的动态类型(也就是Python自动为跟踪对象的类型,不需要在脚本中编写声明语句),Python中变量和对象是如何通过引用关联,垃圾收集的概念,对象共享引用是如何影响多个变量......
  • Python学习手册(第四版)】学习笔记09.3-Python对象类型-分类、引用VS拷贝VS深拷贝、比较
    个人总结难免疏漏,请多包涵。更多内容请查看原文。本文以及学习笔记系列仅用于个人学习、研究交流。这部分稍杂,视需要选择目录读取。主要讲的是对之前的所有对象类型作复习,以通俗易懂、由浅入深的方式进行介绍,所有对象类型共有的特性(例如,共享引用),引用、拷贝、深拷贝,以及比较、......