设计模式第4讲——代理模式(Proxy)

一、什么是代理模式 

代理模式属于结构型设计模式。为其他对象提供一种代理以控制对这个对象的访问。

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

二、分类

代理模式分为三类:1. 静态代理 2. 动态代理 3. CGLIB代理

三、特点

优点:

  1. 代理模式可以隐藏真是对象的实现细节,使客户端无需知晓真实对象的工作方式和结构。
  2. 通过代理类来间接访问真实类,可以在不修改真实类的情况下,对其进行扩展、优化或添加安全措施。
  3. 代理模式实现起来简单,易于扩展和维护,符合面向对象设计原则中的开闭原则。

缺点:

  1. 代理模式可能会引入额外的复杂性和间接性,增加程序设计和维护的难度。
  2. 对象代理可能会降低系统性能,特别是在处理大数据量或频繁调用的情况下,因为代理需要额外的计算和网络通信开销。

四、应用场景 

4.1 生活场景

  • Window是的快捷键。
  • 支票、银行卡。
  • 律师。

4.2 Java场景

  • AOP:通过定义切面、切入点和通知等,Spring AOP在运行时生成代理对象,将切面逻辑织入到目标对象的方法调用中。代理对象在方法调用前后执行附加操作,如日志记录、性能监控等。
  • 动态代理(JDK动态代理、CGLIB代理):当Bean类实现了接口时,Spring使用JDK动态代理来为Bean生成代理对象;当Bean类没有实现接口时,Spring使用CGLIB代理来生成代理对象。

五、代码实现 

5.0 代码结构

下面就以房东和租客为例,分别介绍一下静态代理jdk动态代理cglib代理

5.1 静态代理 

静态代理是一种在代码编写期进行代理类和被代理类的关联的代理方式。

具体实现是创建一个代理类,通常需要实现与被代理类相同的接口或继承被代理类。

房东接口类:Landlord1Service,

注意:静态代理实现它的真实对象只能有一个,多个的话,代理对象不能确定哪个对象需要被代理,会导致报错,JDK动态代理没这个问题

/**
 * 房东
 * @author Created by njy on 2023/5/30
 */
public interface Landlord1Service {
    /**
     * 出租
     * @param money 金额
     * @return
     */
    void rent(Integer money);
}

租客:TenantImpl

/**
 * 租客
 * @author Created by njy on 2023/5/30
 */
@Component
public class TenantImpl implements Landlord1Service {

    @Override
    public void rent(Integer money) {
        System.out.println("租下"+money+"元一个月的房子");
    }
}

静态代理:ProxyImpl

/**
 * 中介
 * @author Created by njy on 2023/5/30
 */
@Component
public class ProxyImpl implements Landlord1Service {

    /**
     * 房东有很多套房子,不想亲自出马了,于是找来了中介
     */
    @Autowired
    private Landlord1Service target;

    /**
     * 优点就是在不改变原来的实现类的情况下对方法实现了增强
     * 缺点是如果原来的接口新增了方法,那么这里也要对应实现新的方法
     * @param money 金额
     * @return
     */
    @Override
    public void rent(Integer money) {
        System.out.println("[静态代理]交中介费");
        target.rent(money);
        System.out.println("[静态代理]中介负责维修管理");
    }
}

测试:

/**
 * @author Created by njy on 2023/5/30
 */
@SpringBootTest
public class TestProxy {
    @Autowired
    private TenantImpl tenant;

    @Autowired
    private ProxyImpl proxy;

    //1.静态代理
    @Test
    void TestStatic(){
        tenant.rent(1000);
        System.out.println();
        proxy.rent(2000);
    }
}

适用场景:

  • 当代理对象只有一个时,可以使用静态代理
  • 当被代理的类的接口比较稳定时,可以使用静态代理
  • 当需要为多个被代理的类提供代理时,会导致代理类过多,不方便管理和维护,所以不建议使用静态代理。

5.2 JDK动态代理

JDK动态代理是一种比较常见的代理方式,它是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成。

房东接口类:Landlord2Service

/**
 * @author Created by njy on 2023/5/30
 */
public interface Landlord2Service {

    /**
     * 出租
     * @param money
     * @return
     */
    void rent(Integer money);
}

租客1:Teant1Impl

/**
 * @author Created by njy on 2023/5/30
 */
@Component
public class Teant1Impl implements Landlord2Service{
    @Override
    public void rent(Integer money) {
        System.out.println("tenant1租下"+money+"元一个月的房子");
    }
}

租客2:Teant2Impl

/**
 * @author Created by njy on 2023/5/30
 */
@Component
public class Tenant2Impl implements Landlord2Service {
    @Override
    public void rent(Integer money) {
        System.out.println("tenant2租下"+money+"元一个月的房子");
    }
}

JDK动态代理:JDKProxy

/**
 * JDK动态代理:就是把代理抽象了一下
 * @author Created by njy on 2023/5/30
 */
public class JDKProxy {

    private Object target;

    public JDKProxy(Object target){
        this.target=target;
    }

    /**
     * 给目标对象生成代理对象
     * @return 代理生成的对象
     */
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                //这里是要实现jdk代理InvocationHandler的接口,lambda表达式
                (proxy,method,args)->{
                    //执行对象方法
                    System.out.println("[JDK动态代理]交中介费");
                    method.invoke(target,args);
                    System.out.println("[JDK动态代理]中介负责维修管理");
                    return null;
                });
    }
}

Test:

/**
 * @author Created by njy on 2023/5/30
 */
@SpringBootTest
public class TestProxy {
    @Autowired
    private Teant1Impl teant1;
    @Autowired
    private Tenant2Impl tenant2;
    //2.JDK动态代理
    @Test
    void TestJDK(){
        Landlord2Service proxyInstance1 = (Landlord2Service) new JDKProxy(teant1).getProxyInstance();
        proxyInstance1.rent(2500);

        System.out.println();
        Landlord2Service proxyInstance2 = (Landlord2Service) new JDKProxy(tenant2).getProxyInstance();
        proxyInstance2.rent(2500);
    }
}

适用场景:

  • 对象必须实现一个或多个接口
  • 代理类的代理方法不需要额外的逻辑

5.3 Cglib代理

CGLIB代理是在运行时动态生成代理类的方式,它使用的库是cglib,和JDK代理相比,它不是动态的生成一个实现了接口的代理类,而是直接在内存中构建一个被代理类的子类,并重写父类的方法来进行代理。

房东类:Landlord3Service

/**
 * @author Created by njy on 2023/5/30
 */
@Component
public class Landlord3Service {
    /**
     * 出租房屋
     * @param money
     * @return
     */
    public void rent(Integer money){
        System.out.println("租下"+money+"元一个月的房子");
    }
}

Cglib代理类:CglibProxy

/**
 * JDKProxy:cglib子类代理工厂
 * 1.代理的类不能为final
 * 2.目标对象的方法如果为final/static,那么就不会被拦截,也不会执行目标对象的业务方法
 * @author Created by njy on 2023/5/30
 */
public class CglibProxy implements MethodInterceptor {

    /**
     * 目标对象
     */
    private final Object target;

    public CglibProxy(Object target){
        this.target=target;
    }

    public Object getProxyInstance(){
        //1.工具类
        Enhancer en=new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("[Cglib代理]交中介费");
        method.invoke(target,objects);
        System.out.println("[Cglib代理]中介负责维修管理");
        return null;
    }
}

测试: 

/**
 * @author Created by njy on 2023/5/30
 */
@SpringBootTest
@RequiredArgsConstructor
public class TestProxy {
    @Autowired
    private Landlord3Service landlord3Service;
    //3.Cglib代理
    @Test
    void TestCglib(){
        Landlord3Service proxyInstance = (Landlord3Service) new CglibProxy(landlord3Service).getProxyInstance();
        proxyInstance.rent(3000);
    }
}

​ 

 适用场景:

  • 被代理的类没有实现接口或者无法实现接口
  • 代理类的代理方法需要进行额外的逻辑,如事务处理等。

六、总结

  • 对于没有实现接口的类,只能使用CGLIB代理
  • 对于实现了接口的类,可以使用JDK代理或者CGLIB代理,如果要求比较高的话,建议使用JDK代理。
  • 对于单个代理类的情况,并且被代理类实现了接口,可以使用静态代理。
  • 对于多个被代理类的情况,建议使用JDK代理或CGLIB代理。

END:更多设计模式的介绍,推荐移步至👉 23种设计模式学习导航(Java完整版)👈 

  • 15
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
1、 FACTORY —追 MM 少不了请吃饭了, 麦当劳的鸡翅和肯德基的鸡翅都是 MM 爱吃的东西, 虽然口味有所不同, 但不管你带 MM 去麦当劳或肯德基, 只管向服务员说“来四个鸡翅”就行 了。麦当劳和肯德基就是生产鸡翅的 Factory 工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消 工厂模式 费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如 何创建及如何向客户端提供。 2、BUILDER — MM 最爱听的就是“我爱你”这句话了,见到不同地方的 MM,要能够用她们的 、 方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到 MM 我只要按对应的键, 它就能够用相应的语言说出“我爱你”这句话了, 国外的 MM 也可以轻松 搞掂,这就是我的“我爱你”builder。 (这一定比美军在 伊拉克用的翻译机好卖) 建造模式: 从而使一个建造过程生成具有不 建造模式 将产品的内部表象和产品的生成过程分割开来, 同的内部表象的产品对象。 建造模式使得产品内部表象可以独立的变化, 客户不必知道产品 内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。 3、FACTORY METHOD —请 MM 去麦当劳吃汉堡,不同的 MM 有不同的口味,要每个都记住 、 是一件烦人的事情,我一般采用 Factory Method 模式,带着 MM 到服务员那儿,说“要一个 汉堡”,具体要什么样的汉堡呢,让 MM 直接跟服务员说就行了。 工厂方法模式: 而是将具体创建的工作交给子类去做, 工厂方法模式 核心工厂类不再负责所有产品的创建, 成为一个抽象工厂角色, 仅负责给出具体工厂类必须实现的接口, 而不接触哪一个产品类应 当被实例化这种细节。 4、 、 PROTOTYPE —跟 MM 用 QQ 聊天, 一定要说些深情的话语了, 我搜集了好多肉麻的情话, 需要时只要 copy 出来放到 QQ 里面就行了, 这就是我的情话 prototype 了。 (100 块钱一份, 你要不要) 原始模型模式: 原始模型模式 通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原 型对象的方法创建出更多同类型的对象。 原始模型模式允许动态的增加或减少产品类, 产品 类不需要非得有任何事先确定的等级结构, 原始模型模式适用于任何的等级结构。 缺点是每 一个类都必须配备一个克隆方法。 5、 、 SINGLETON —俺有 6 个漂亮的老婆, 她们的老公都是我, 我就是我们家里的老公 Sigleton, 她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事) 单例模式: 而且自行实例化并向整个系统提供这个实 单例模式 单例模式确保某一个类只有一个实例, 例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 结构型模式 6、ADAPTER —在朋友聚会上碰到了一个美女 Sarah,从香港来的,可我不会说粤语,她不 、 会说普通话,只好求助于我的朋友 kent 了,他作为我和 Sarah 之间的 Adapter,让我和 Sarah 可以相互交谈了(也不知道他会不会耍我) 适配器模式: 从而使原本因接口原因不 适配器模式 把一个类的接口变换成客户端所期待的另一种接口, 匹配而无法一起工作的两个类能够一起工作。 适配类可以根据参数返还一个合适的实例给客 户端。 7、BRIDGE —早上碰到 MM,要说早上好,晚上碰到 MM,要说晚上好;碰到 MM 穿了件新 、 衣服, 要说你的衣服好漂亮哦, 碰到 MM 新做的发型, 要说你的头发好漂亮哦。 不要问我“早 上碰到 MM 新做了个发型怎么说”这种问题,自己用 BRIDGE 组合一下不就行了 桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关 桥梁模式 联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是 继承关系,从而使两者可以独立的变化。 8、COMPOSITE —Mary 今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店, 、 你自己挑。”“这件 T 恤挺漂亮,买,这条裙子好看,买,这个包也不错, 买。”“喂,买了 三件了呀,我只答应送一件礼物的哦。”“什么呀,T 恤加裙子加包包,正好配成一套呀,小 姐,麻烦你包起来。”“……”,MM 都会用 Composite 模式了,你会了没有? 合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就 合成模式 是一个处理对象的树结构的模式。 合成模式把部分与整体的
设计模式是一种经过总结、优化的、可重复使用的设计经验的总结。它是软件工程中一种解决特定问题的可复用方案。设计模式不是一成不变的,而是可以根据不同的需求进行变化,以达到最佳的效果。设计模式可以提高程序的可读性、可维护性、可扩展性,同时也可以提高程序的性能和稳定性。 设计模式可以分为三种类型:创建型模式、结构型模式和行为型模式。创建型模式用于描述对象的创建过程,结构型模式用于描述对象之间的关系,行为型模式用于描述对象的行为和交互。本篇文章将介绍一些常用的设计模式。 1. 工厂模式(Factory Pattern) 工厂模式是一种常用的创建型模式,它使用一个工厂方法来创建对象,而不是通过直接调用构造函数。工厂模式可以隐藏对象的创建过程,使代码更加灵活和易于维护。工厂模式可以分为简单工厂模式、工厂方法模式和抽象工厂模式。 2. 单例模式(Singleton Pattern) 单例模式是一种创建型模式,它保证一个类只有一个实例,并提供一个全局访问点。单例模式可以保证对象的唯一性,避免了多个实例对系统资源的浪费。 3. 代理模式Proxy Pattern) 代理模式是一种结构型模式,它为一个对象提供一个代理,以控制对这个对象的访问。代理模式可以增加对象的安全性,降低对象的访问成本,同时也可以提高程序的灵活性和可扩展性。 4. 装饰器模式(Decorator Pattern) 装饰器模式是一种结构型模式,它动态地给一个对象添加一些额外的职责,而不需要修改这个对象的代码。装饰器模式可以避免使用子类来扩展对象的功能,从而使代码更加灵活和可扩展。 5. 观察者模式(Observer Pattern) 观察者模式是一种行为型模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象状态发生改变时,会通知所有的观察者对象,使它们能够及时更新自己的状态。 以上是五种常用的设计模式,它们在软件开发中都有着广泛的应用。设计模式可以帮助我们更好地组织代码、降低程序的耦合度、提高程序的可扩展性和可维护性,是一种非常重要的编程技巧。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橡 皮 人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值