设计模式--代理模式【结构型模式】

设计模式的分类

我们都知道有 23 种设计模式,这 23 种设计模式可分为如下三类:

  • 创建型模式(5 种):单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
  • 结构型模式(7 种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
  • 行为型模式(11 种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

在这里插入图片描述

设计模式系列文章传送门

设计模式的 7 大原则

设计模式–单例模式【创建型模式】

设计模式–工厂方法模式【创建型模式】

设计模式–抽象工厂模式【创建型模式】

设计模式–建造者模式【创建型模式】

设计模式–原型模式【创建型模式】

设计模式–适配器模式【结构型模式】

设计模式–装饰器模式【结构型模式】

什么是代理模式

代理模式属于结构型设计模式,其核心思想是为其他对象提供一种代理来完成对这个对象的访问,代理对象可以在客户端和目标对象之间起到中介的作用。

代理模式的分类

代理模式可以分为三类,如下:

  • 静态代理:需要程序开发者显示的创建代理类代码,其代理类和被代理的对象在编译期间就确定下来。
  • 动态代理:代理类在程序运行时通动态生成,无需程序开发者显式地创建代理类。
  • 虚拟代理 :虚拟代理作为创建开销大的对象的代理,会直到我们真正需要使用一个对象的时候才创建它,当对象在创建前和创建中时,由虚拟代理地来扮演对象的,对象创建后,代理就会将请求直接委托给对象。

代理模式的优缺点

优点:

  • 代理模式可以隐藏真是对象的实现细节,使客户端无需知晓真实对象的工作方式和结构,简化了客户端。
  • 通过代理类来间接访问真实类,可以在不修改真实类的情况下,对其进行进行功能扩展,可以在访问对象之前或之后执行一些额外的操作。
  • 代理模式可以对真实对象进行保护,只允许特定的客户端访问真实对象,避免了真实对象的暴露,从一定程度上体现了安全性。
  • 代理模式可以延迟加载真实对象,当需要真实对象时才进行创建和初始。

缺点:

  • 增加了系统的复杂性,可以说是设计模式的通用缺点,具体就看是否是利大于弊了。
  • 在一定程度上增加了系统的性能消耗,因为代理模式在访问对象时引入了额外的间接层,例如需要使用到反射的技术栈,这可能会对系统的响应时间产生影响。

代理模式的组成

  • 抽象主题(Subject)角色:通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 具体主题(Real Subject)角色:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,也是最终要引用的对象,也叫被代理对象。
  • 代理(Proxy)角色:提供了与真实主题相同的接口,其内部含有对具体主题的引用,它可以访问扩展具体主题的功能。

静态代理

我们以生活中的租房为案例来演示静态代理,假设我是房东我有房子需要出租,但是我没有时间去打理出租的这个过程,因为我委托访房产中介来代理我出租房子,这里面我要出租房子就是具体主题,而中介就是代理角色。

House (抽象主题)

定义了一个需要出租房屋的方法,也就是抽象主题,代码如下:

public interface House {

    //出租房子
    void rentHouse();

}

MyHouse(具体主题)

我有房子要出租,是具体主题,其实现了抽象主题,代码如下:

public class MyHouse implements House{

    @Override
    public void rentHouse() {
        System.out.println("租房的客户来了");
    }
}

HouseRentProxy(静态代理)

我要出租房子,而我又没有时间开打理,因此将此业务委托给中介代理,也就是代理对象,代码如下:

public class HouseRentProxy implements House {

    private MyHouse myHouse;

    public HouseRentProxy(MyHouse myHouse) {
        this.myHouse = myHouse;
    }

    @Override
    public void rentHouse() {
        System.out.println("委托代理中介来帮我组房子");
        myHouse.rentHouse();
    }
}

可以看到,静态代理对象内部含有对真实主题的引用

ClientHouse(客户端)

假设有一个客户要来租房,我通过代理对象完成房屋的出租,代码如下:

public class ClientHouse {

    public static void main(String[] args) {
        //具体对象
        MyHouse myHouse = new MyHouse();
        //代理对象
        HouseRentProxy houseRentProxy = new HouseRentProxy(myHouse);
        //同多代理对象来租房
        houseRentProxy.rentHouse();
    }

}

执行结果如下:

委托代理中介来帮我组房子
租房的客户来了

可以看到我们通过代理对象完成了房屋的出租。

静态代理使用场景

  • 当代理对象较少时候可以使用静态代,因为静态代理需要进行编码开发,如果被代理对象较多,会导致代理类很多。
  • 有相同的功能需求的时候可以使用静态代理,这一条是对上一条的补充,只有不用的功能需求才需要不同的代理类,因此静态代理适用用于有相同功能需求的场景。

动态代理

在 Java 中动态代理的实现又分为 JDK 动态代理和 CGLib 动态代理,我们接下来分开分析。

JDK 动态代理

JDK 动态代理是基于接口的代理方式,它是在程序运行时动态生成代理类,也就是说我们在编写代码时并不知道具体代理的是什么类,而是在程序运行时动态生成,是 Java 标准库中提供的一种代理方法,代理对象实现和原始类一样的接口,并将方法调用转发给被代理对象,同时还可以在方法调用前后执行额外的增强处理,JDK 动态代理通过反射机制实现代理功能。

JDK 动态代理的代理类实现了 InvocationHandler 接口,其目的是对具体主题的方法的增强,InvocationHandler 接口中只有一个 invoke 方法,动态代理对象中的映射方法在执行时都是调用的 InvocationHandler 接口中的 invoke 方法,在调用 invoke 方法时,动态代理对象会将 被代理对象的方法和动态代理对象映射的方法的参数传递给 InvocationHandler 的 invoke 方法, invoke 方法的实现是由开发人员编写的,这样开发人员就可以在被代理对象的方法执行前后进行功能增强。

House (抽象主题)

定义了一个需要出租房屋的方法,也就是抽象主题,代码如下:

public interface House {

    //出租房子
    void rentHouse();

}

MyHouse(具体主题)

我有房子要出租,是具体主题,其实现了抽象主题,代码如下:

public class MyHouse implements House{

    @Override
    public void rentHouse() {
        System.out.println("租房的客户来了");
    }
}

MyHouseJdkProxy(动态代理)

JDK 动态代理类,代码如下:

public class MyHouseJdkProxy implements InvocationHandler {

    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理中介帮我找到了租户");
        Object invoke = method.invoke(target, args);
        System.out.println("代理中介帮我完成了租赁签约");
        return invoke;
    }
}

ClientHouse(客户端)

客户端使用 JDK 动态代理类完成房屋出租功能,代码如下:

public class ClientHouse {

    public static void main(String[] args) {
        //被代理类对象
        MyHouse myHouse = new MyHouse();
        //被代理对象类加载器
        ClassLoader classLoader = myHouse.getClass().getClassLoader();
        //被代理对象的方法
        Class<?>[] interfaces = myHouse.getClass().getInterfaces();
        //获取代理对象
        MyHouseJdkProxy myHouseJdkProxy = new MyHouseJdkProxy(myHouse);
        //生成代理对象
        House myHouseProxy = (House) Proxy.newProxyInstance(classLoader, interfaces, myHouseJdkProxy);
        myHouseProxy.rentHouse();
    }

}

动态代理执行结果如下:

代理中介帮我找到了租户
租房的客户来了
代理中介帮我完成了租赁签约

可以看到我们使用代理类完成了房屋的出租,并且完成了房屋出租前后的增加实现。

CGLIB 动态代理

CGLIB 代理是在运行时动态生成代理类的方式,它使用的是 cglib 库,和 JDK 动态代理相比,它不是动态的生成一个实现了接口的代理类,
而是在运行时动态生成目标类的子类作为代理类,并重写其中的方法来实现代理功能,与 JDK 动态代理不同,CGLIB动态代理没有实现接口,CGLIB 代理是基于类的代理。

CGLIB 代理实现步骤:

  1. 首先我们需要一个具体主题,也就是被代理类,就是一个业务普通类,区别于 JDK 动态代理,该类不需要实现接口。
  2. 然后我需要编写一个增强类,或者说代理类,增强的方法基于该类实现,该类需要实现 CGLIB 的 MethodInterceptor 接口,并重写其 intercept 方法,在该方法中实现增强逻辑。
  3. 在客户端代码中我们要基于 Enhancer 生成目标对象的代理对象。

这里我们同样使用出租房屋这个场景来演示 CGLIB 代理。

MyHouse(具体主题)

我们根据出租房屋抽象出具体主题 MyHouse,代码如下:

public class MyHouse {

    public void rentHouse() {
        System.out.println("租房的客户来了");
    }
}

可以看到这里区别于 JDK 动态代理,这里没有实现接口,就只是定义了一个普通的类。

MyHouseCglibProxy(动态代理)

CGLIB 动态代理增强类,代码如下:

public class MyHouseCglibProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理中介帮我找到了租户");
        Object invokeSuper = methodProxy.invokeSuper(object, objects);
        System.out.println("代理中介帮我完成了租赁签约");
        return invokeSuper;
    }
}

ClientHouse(客户端)

客户端使用 CGLIB 动态代理类完成房屋出租功能,代码如下:

public class ClientHouse {

    public static void main(String[] args) {
        //cglig 代理的增强类
        Enhancer enhancer=new Enhancer();
        //设置被代理类 cgliba 根据该类去生成子类
        enhancer.setSuperclass(MyHouse.class);
        //设置代理类 也就是增强方法
        enhancer.setCallback(new MyHouseCglibProxy());
        //生成代理对象
        MyHouse myHouseProxy = (MyHouse)enhancer.create();
        myHouseProxy.rentHouse();


    }

}

执行结果如下:

代理中介帮我找到了租户
租房的客户来了
代理中介帮我完成了租赁签约

可以看到我们使用 CGLIB 代理实现了功能增强

动态代理适用场景

  • 日志记录,如果我们需要对某个方法进行日志记录,使用动态代理在方法调用前后自动记录日志,非常的方便。
  • 方法功能增强,动态代理可以在不修改原有对象的基础上,为对象添加额外的功能,比如事务管理、异常处理等,适合对方法进行功能增强的场景。
  • 敏感对象的保护,对于一些不想暴露的对象,我们使用动态代理的方式来访问,在一定程度上起到了对象的保护作用。

总结:本篇我们简单分享了结构型设计模式–代理模式,并举例分析了静态代理和动态代理的具体实现,希望可以帮助到有需要的朋友。

如有不正确的地方欢迎各位指出纠正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值