掘金 后端 ( ) • 2022-01-26 12:01

theme: simplicity-green

这是我参与2022首次更文挑战的第9天,活动详情查看:2022首次更文挑战

代理模式

概念

代理模式(Proxy Pattern)是指一个类代表另一个类的功能,为其他对象提供一种代理,并由代理对象控制对原对象的引用。

总的来说就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。从类的加载过程中来看,代理模式是属于结构型模式。

代理模式主要适用于在某些情况下,一个对象不适合或者不能直接调用另外一个对象,而代理模式的代理对象就在客户端和用户之间起到了中介连接的作用。比如现实生活中租房和房东之间的代理对象就是房屋中介、淘宝店家和用户之间的代理对象就是快递点等例子。

代理模式实现原理

  • 代理模式的通用类图

image.png

通过上图可以得知,ISubject是一个抽象接口,RealSubject是实现方法类,具体的业务执行,而TikTokProxy是RealSubject的代理对象,MainTest直接初始化TikTokProxy,就可以调用RealSubject中的Request方法。

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

  • 代理模式的角色

    • 抽象主题角色(ISubject):声明主题与代理对象的共同接口方法,该类可以是接口类或者是抽象类

    • 代理主题角色(Proxy):主要是代理类,内部定义了被代理类的引用,完全具备被代理类的代理权,在代理主题中可以做到代码增强功能。

    • 真实主题角色(RealSubject):真实主体角色,主要是被代理类,该类定义了代理所表示的真实对象,是负责执行系统的真正的逻辑业务对象

代理模式应用场景

生活中的应用场景有很多,比如租房中介、黄牛、快递等,都是代理模式的在现实中的实际体现。当无法或者不想直接引用某个对象或者访问某个对象比较困难时,就可以通过代理对象进行访问这些对象。使用代理模式主要有亮点:1、保护目标对象(被代理对象),2、增强目标对象

在代码中也有很多的体现,比如

  • Mybatis的Mapper是如何进行生成的

  • Alibaba Seata的DataSourceProxy是什么

  • DruidDataSource的监控链是如何进行代理的

  • Spring如何进行代理的

静态代理

静态代理一般情况下需要代理对象和目标对象实现一样的接口

代码实现

  • 接口类(ISubject)
public interface ISubject {
    void request();
}
  • 目标对象(RealSubject)
public class RealSubject implements ISubject {
    @Override
    public void request() {
        System.out.println("人,request......");
    }
}
  • 代理对象(TikTokProxy)
public class TikTokProxy implements ISubject {
    private ISubject subject;

    public TikTokProxy(ISubject subject) {
        this.subject = subject;
    }

    @Override
    public void request() {
        before();
        subject.request();
        after();
    }
    private void after() {
        System.out.println("after增强");
    }
    private void before() {
        System.out.println("before增强");
    }
}

通过上述代码可以知道。代理对象TikTokProxy引用了目标对象ISubject,并在重写request方法,同时在该方法中进行了方法增强。

静态代理的优点

  • 代理类作为客户端和被代理类之间的中介,起到了保护被代理类的作用

  • 通过接口对代理类和被代理类进行解耦,降低了系统的耦合度

静态代理的缺点

  • 代码冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类。

  • 代码不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。

动态代理

动态代理利用了JDK API,动态地在内存中构建代理对象,从而实现对目标对象的代理功能。动态代理又被称为JDK代理或接口代理。

动态代理对象不需要实现接口对象,但是必须保证被代理对象必须实现接口

代码实现

  • 接口类
public interface SellTikTok {
    void sell();
}
public interface ManTikTok {
    void tikTok();
}
  • 目标类
public class TikTok implements ManTikTok,SellTikTok {
    @Override
    public void tikTok() {
        System.out.println("tikTok....");
    }

    @Override
    public void sell() {
        System.out.println("sell....");
    }
}
  • jdk代理对象
public class JdkTikTokProxy implements InvocationHandler {

    private T target;
    public JdkTikTokProxy(T target) {
        this.target = target;
    }
    public static  T getProxy(T t) {
        Object o = Proxy.newProxyInstance(t.getClass().getClassLoader()
                , t.getClass().getInterfaces()
                , new JdkTikTokProxy(t));
        return (T) o;
    }

    @Override
    public Object invoke(Object proxy,
                         Method method,
                         Object[] args) throws Throwable {
        System.out.println("真正执行被代理对象的方法");
        Object invoke = method.invoke(target, args);
        System.out.println("返回值:" + invoke);
        return null;
    }
}

通过上述代码可以知道,将被代理对象传入到JdkTikTokProx中,JdkTikTokProx通过invoke方法就可以获取到真正的被代理对象的代理权。

动态代理的优点

  • 代理类在程序运行时由反射自动生成,无需我们手动编写代理类代码,简化编程工作

  • 一个动态代理类invoke就能代理多个被代理类。无需更改动态代理类的内容

动态代理的缺点

  • 动态代理只能代理实现了接口的类,而不能代理实现抽象类的类

  • 通过反射调用被代理类的方法,效率低,性能低下

静态代理和动态代理的区别

  • 静态代理只能通过手动完成代理操作,如果被代理类增加了新的方法,则代理类需要同步增加,违背开闭原则。

  • 动态代理采用在运行时动态生成代码的方式,取消了对被代理类的扩展限制,遵循开闭原则。

  • 若动态代理要对目标类的增强逻辑进行扩展,结合策略模式,只需要新增策略类便可完成,无需修改代理类的代码。

代理模式的优缺点

优点

  • 代理模式能将代理对象与目标对象分离,可以适当的结耦合

  • 在一定的程度上降低了系统的耦合性,扩展性得到了很大的提升

  • 可以对目标对象进行保护,避免目标对象被入侵

  • 可以对目标对象不足的功能进行特定的功能增强

缺点

  • 代理模式会增加系统多余的类

  • 在调用类和被调用类之间增加一个代理对象,访问效率较低

  • 增加系统代码复杂度