【设计模式】代理模式

发布于:2025-08-10 ⋅ 阅读:(17) ⋅ 点赞:(0)

1. 代理模式

1.1. 概述

代理模式就是为目标对象提供一种代理以控制对目标对象的访问。它的作用就是通过一个代理类,让我们在调用目标对象的时候,不再是直接进行调用,而是调用代理对象,通过代理对象来间接调用。
在某些情况下,一个对象不适合或者不能直接调用另一个对象,我们就可以通过代理对象在客户端和目标对象之间起到中介的作用。
使用代理前:
在这里插入图片描述
使用代理后:在这里插入图片描述

1.2. 主要角色

  1. 业务接口类,表示目标对象和代理对象要实现什么方法。
  2. 业务实现类,表示真正的业务执行,也就是目标对象。
  3. 代理类,当外部来调用目标对象时,实际上调用的就是代理对象,通过代理对象间接的调用目标对象。

通过代理对象的间接调用,我们就可以在代理类中编写一个额外的代码,从而在不改变原有代码的前提下,对方法进行增强。不仅减少了代码的耦合性,还提高了代码的复用性。

1.3. 分类

代理模式分为三类,主要是静态代理和动态代理。下面我们将详细叙述一下静态代理和动态代理。

2. 静态代理

静态代理需要程序猿手动创建代理类再对其进行编译,在编译时期就已经将代理类创建好了。
以房东和中介为例,房东属于目标对象,中介属于代理对象。在出租房子前,中介就确定好了目标对象是谁,也就是确定好了要出租哪个房东的房子。客户来了之后,这个要租哪个房东的房子就不能再修改了。如果客户想要租另一个房东的房子,那么就需要中介再去协商,也就是程序猿要再写一个代理类表示另一个房东。

2.1. 业务接口

/**
 * 业务接口类,表示目标对象和代理对象要实现什么方法
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public interface Subject {
    /**
     * 租房子
     */
    void rent();
}

2.2. 业务实现类

/**
 * 目标对象,表示业务要执行什么内容
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("房东A出租房子");
    }
}

2.3. 代理类

/**
 * 代理对象,当客户端访问时,通过调用代理对象从而间接调用目标对象
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class ProxySubject implements Subject {
    private final Subject realSubject;

    public ProxySubject(Subject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void rent() {
        System.out.println("代理对象执行额外方法");
        realSubject.rent();
        System.out.println("代理对象执行额外方法");
    }
}

2.4. 启动类

/**
 * 启动类
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class Main {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(realSubject);
        proxySubject.rent();
    }
}

执行结果

对于上述代码来说,我们代理的是房东A的房子。当客户想要租房东B的房子,那么就需要我们重写一个代理类。实际上,这是非常冗余的,或者说代码的复用性极低。这时就需要我们提出动态代理来解决了。

3. 动态代理

动态代理不需要程序猿手动为每个目标类创建一个代理类,而是把这个工作推迟到程序运行时,由程序动态的创建生成代理类。
以房东和中介为例,房东属于目标对象,中介属于代理对象。此时,无论客户端过来想租哪个房东的房子,中介都不需要去商量,而是直接带着客户就去看了,这个的实际原理就是客户端想去看哪个房子,那么中介实时的就去生成代理了。所以动态代理相对于静态代理较为灵活,不需要在程序运行前就决定好代理的内容,而是在运行期间动态地进行决定。

动态代理存在JDK动态代理和CGLIB动态代理两种方式。

3.1. JDK动态代理

  1. 定义业务接口及其实现类;
  2. 实现InvocationHandler接口,并重写 invoke方法。在invoke方法中我们就可以调用目标方法,并且可以定义一些自定义业务逻辑。
  3. 通过Proxy.newProxyInstance(ClassLoader loader, Class<?> interfaces, InvocationHandler h)方法创建代理对象。

3.1.1 业务接口

/**
 * 业务接口类,表示目标对象和代理对象要实现什么方法
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public interface Subject {
    /**
     * 租房子
     */
    void rent();
}

3.1.2 业务实现类

/**
 * 目标对象,表示业务要执行什么内容
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("房东A出租房子");
    }
}

3.1.3 JDKInvocation

/**
 * JDKInvocation类,用来实现动态代理
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class JDKInvocation implements InvocationHandler {
    private final Subject realSubject;

    public JDKInvocation(Subject realSubject) {
        this.realSubject = realSubject;
    }

    /**
     * @param proxy 代理对象
     * @param method 要调用的目标类的方法
     * @param args 调用目标类方法时要传的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理对象执行额外方法");
        Object invoke = method.invoke(realSubject, args);
        System.out.println("代理对象执行额外方法");
        return invoke;
    }
}

3.1.4. 启动类

/**
 * 启动类
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class Main {
    public static void main(String[] args) {
        Subject subject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
                subject.getClass().getClassLoader(), // 类加载器,用于加载代理对象
                new Class[]{Subject.class}, // 被代理类实现的一些接口
                new JDKInvocation(subject) // 实现了InvocationHandler接口的对象
        );
        proxy.rent();
    }
}

在这里插入图片描述

3.2. CGLIB动态代理

  1. 定义一个接口和实现类。
  2. 实现MethodInterceptor并重写intercept方法。intercept用于增强目标方法,和JDK动态代理中的invoke方法作用类似。
  3. 通过Enhancer类的create创建代理类。

3.2.1. 业务接口

/**
 * 业务接口类,表示目标对象和代理对象要实现什么方法
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public interface Subject {
    /**
     * 租房子
     */
    void rent();
}

3.2.2. 业务实现类

/**
 * 目标对象,表示业务要执行什么内容
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class RealSubject implements Subject {
    @Override
    public void rent() {
        System.out.println("房东A出租房子");
    }
}

3.2.3. CGLibInterceptor

/**
 * CGLibInterceptor类,用来实现动态代理
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class CGLibInterceptor implements MethodInterceptor {
    /**
     * @param target 目标对象
     * @param method 目标对象的目标方法
     * @param objects 调用目标方法时要传的参数
     * @param methodProxy 代理对象的代理方法
     *
     */
    @Override
    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理对象执行额外方法");
        Object invoke = methodProxy.invokeSuper(target, objects);
        System.out.println("代理对象执行额外方法");
        return invoke;
    }
}

3.2.4. 启动类

/**
 * 启动类
 * @author WangBinZe
 * @version 1.0
 * @date 2025/8/5
 */
public class Main {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(realSubject.getClass());
        enhancer.setCallback(new CGLibInterceptor());

        RealSubject proxy = (RealSubject) enhancer.create();
        proxy.rent();
    }
}

在这里插入图片描述

3.3. JDK和CGLIB的区别

  1. JDK动态代理基于反射Proxy类实现;CGLIB动态代理基于ASM字节码操作框架实现。
  2. JDK动态代理要求目标类必须实现一个接口;CGLIB通过继承方式实现代理,无需目标类实现接口。
  3. JDK动态代理生成的是和目标类相同接口的新类;CGLIB动态代理则是生成被代理类的子类。

对于JDK动态代理来说,底层使用的是反射机制,默认会继承一个Proxy的类,或者说Proxy是所有JDK动态代理的父类。由于Java是单继承的特性,因此我们不再可以使用继承的方式来实现代理,而是基于接口的方式来实现代理。

如下图所示,是CGLIB动态代理类实现的接口和继承的父类。
在这里插入图片描述
如下图所示,是JDK动态代理类实现的接口和继承的父类。
在这里插入图片描述

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

  1. 静态代理需要程序猿手动创建代理类;动态代理则是程序猿实现一定内容后,由程序动态常见代理类。
  2. 静态代理类是编译时生成的;动态代理则是运行期间由程序动态生成。
  3. 静态代理每个目标对象都需要程序员手动编写一个类,代码量庞大;动态代理则是一套代码可以生成多个代理类,代码量少。
  4. 静态代理性能较好,运行期间直接调用即可;动态代理性能较差,需要反射或者使用AMS等机制实现。
  5. 静态代理适合目标类较少,关系固定的情况;动态代理则是适合多个类都需要代理的情况。

网站公告

今日签到

点亮在社区的每一天
去签到