结构型模式-代理模式

代理模式(Proxy Pattern)是一种结构型设计模式,它为对象提供一个替身或占位符,以便控制对对象的访问。通过代理模式,客户端可以间接地与真实对象交互,而无需直接处理复杂的逻辑或操作。

核心角色

代理模式通常包含以下三个角色:

  • 抽象主题(Subject)

    定义代理和真实对象的共同接口,客户端通过这个接口与代理对象和真实对象交互。

  • 真实主题(RealSubject)

    具体实现抽象主题接口的类,表示被代理的对象,负责处理实际业务逻辑。

  • 代理(Proxy)

    实现抽象主题接口,内部持有真实主题的引用,负责控制对真实对象的访问,可能会在访问前后增加额外的功能(如权限校验、延迟加载、日志记录等)。

分类

  • 静态代理

    代理类在编译时就已经生成,代理类的代码由开发者手动编写或通过工具生成。

    优点:简单直观。

    缺点:每增加一个真实类都需要编写对应的代理类,维护工作量大。

  • 动态代理

    代理类在运行时动态生成,利用反射机制实现。Java 中常用的动态代理实现有两种:

    JDK 动态代理(基于接口)

    CGLIB 动态代理(基于继承)

    优点:灵活、可以复用通用逻辑。

    缺点:实现较复杂,性能可能略低。

静态代理示例

java 复制代码
// 抽象主题
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理真实请求...");
    }
}

// 代理类
class ProxySubject implements Subject {
    private RealSubject realSubject;

    @Override
    public void request() {
        if (realSubject == null) {
            realSubject = new RealSubject();
        }
        System.out.println("代理开始处理...");
        realSubject.request();
        System.out.println("代理处理完成...");
    }
}

// 测试
public class StaticProxyDemo {
    public static void main(String[] args) {
        Subject proxy = new ProxySubject();
        proxy.request();
    }
}
2. 动态代理示例(JDK)
java
复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象主题
interface Subject {
    void request();
}

// 真实主题
class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("处理真实请求...");
    }
}

// 动态代理处理器
class ProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("动态代理开始处理...");
        Object result = method.invoke(target, args);
        System.out.println("动态代理处理完成...");
        return result;
    }
}

// 测试
public class DynamicProxyDemo {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
            realSubject.getClass().getClassLoader(),
            realSubject.getClass().getInterfaces(),
            new ProxyHandler(realSubject)
        );
        proxy.request();
    }
}

CGLIB 动态代理(基于继承)

CGLIB 动态代理是通过继承方式实现的动态代理,它是 JDK 动态代理的一种补充,尤其在被代理类没有实现接口时非常有用。CGLIB(Code Generation Library)基于字节码生成技术,通过扩展被代理类来实现代理功能。

CGLIB 动态代理的特点

  • 基于继承:代理类是目标类的子类,因此目标类不能是 final 类型(因为 final 类无法被继承)。
  • 无需接口:不同于 JDK 动态代理,CGLIB 不依赖接口,可以代理任何普通类。
  • 性能较高:CGLIB 在生成代理类的运行性能上比 JDK 动态代理更快,但在生成代理类的初始性能上稍慢。

使用场景

  • 目标类没有接口。
  • 需要对方法调用添加通用逻辑,如日志、权限校验、事务管理等。

CGLIB 动态代理的实现步骤

  • 引入 CGLIB 依赖(如果未集成 Spring,可以单独引入)。
  • 定义目标类(无需实现接口)。
  • 创建 MethodInterceptor,用来拦截方法调用。
  • 使用 Enhancer 生成代理类。
xml 复制代码
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

// 目标类
class RealSubject {
    public void request() {
        System.out.println("处理真实请求...");
    }
}

// 自定义 MethodInterceptor
class ProxyInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("CGLIB 代理:开始处理...");
        Object result = proxy.invokeSuper(obj, args); // 调用父类方法
        System.out.println("CGLIB 代理:处理完成...");
        return result;
    }
}

// 测试
public class CGLIBProxyDemo {
    public static void main(String[] args) {
        // 创建 Enhancer
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class); // 设置目标类
        enhancer.setCallback(new ProxyInterceptor()); // 设置回调

        // 生成代理类
        RealSubject proxy = (RealSubject) enhancer.create();

        // 调用代理方法
        proxy.request();
    }
}

注意事项

  • 目标类不能是 final 类:CGLIB 通过继承实现代理,因此被代理的类不能用 final 修饰。
  • 目标方法不能是 final 或 static:因为 final 方法不能被重写,而 static 方法属于类而非对象,也无法被代理。
  • 性能影响:虽然 CGLIB 性能较高,但它的字节码生成过程稍有开销,不适合频繁创建代理的场景。

与 JDK 动态代理的对比

特性 JDK动态代理 CGLIB 动态代理
实现方式 基于接口 基于继承
是否需要接口 必须有接口 无需接口
代理机制 通过反射调用目标对象的方法 通过生成子类并重写方法
性能 代理类生成速度较快,运行速度略慢 代理类生成速度较慢,运行速度较快
限制 无法代理没有实现接口的类 无法代理 final 类和 final 方法

CGLIB 动态代理非常适合在目标类未实现接口或需要高性能场景下使用,但需注意其基于继承的限制。

适用场景

  • 控制对象访问:如权限校验或安全检查。
  • 延迟加载:如在访问大对象时延迟其初始化。
  • 记录日志:在方法调用前后添加日志。
  • 远程代理:为不同地址空间的对象提供访问。
  • 缓存代理:对结果进行缓存以提升性能。
  • 通过代理模式,可以有效地对对象的访问进行控制和增强,是软件设计中常用的一种模式。
相关推荐
春风十里不如你952721 小时前
【设计模式】【结构型模式(Structural Patterns)】之代理模式(Proxy Pattern)
设计模式·代理模式
编程、小哥哥2 天前
设计模式之代理模式(模拟mybatis-spring中定义DAO接口,使用代理类方式操作数据库原理实现场景)
设计模式·mybatis·代理模式
小白不太白9503 天前
设计模式之 代理模式
设计模式·代理模式
吾与谁归in5 天前
【C#设计模式(13)——代理模式(Proxy Pattern)】
设计模式·c#·代理模式
OkeyProxy6 天前
什麼是ISP提供的公共IP地址?
代理模式·proxy模式·ip地址·isp·海外ip代理
kikyo哎哟喂6 天前
Java 代理模式详解
java·开发语言·代理模式
hxj..6 天前
【设计模式】代理模式
java·设计模式·代理模式·动态代理
武子康7 天前
Java-05 深入浅出 MyBatis - 配置深入 动态 SQL 参数、循环、片段
java·sql·设计模式·架构·mybatis·代理模式
武子康8 天前
Java-04 深入浅出 MyBatis - SqlSessionFactory 与 SqlSession DAO与Mapper 代理模式
java·mysql·spring·mybatis·springboot·代理模式