Spring框架之代理模式 (Proxy Pattern)

代理模式(Proxy Pattern)详解

代理模式(Proxy Pattern)是一种结构型设计模式,主要用于为某对象提供一个代理对象,以控制对该对象的访问。代理对象通常在目标对象前进行操作,从而增强目标对象的功能。这种模式为原对象提供了一种间接访问的方式,可以增加额外的功能(如权限控制、延迟加载、日志记录等)而不修改目标对象的代码。

1. 代理模式的基本概念

1.1 什么是代理模式?

代理模式的核心思想是使用代理对象来控制对目标对象的访问。代理对象与目标对象实现相同的接口,因此它们可以相互替换。代理对象通常包含对实际目标对象的引用,并可以在调用目标对象的方法之前或之后添加额外的操作。

1.2 代理模式的结构

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

  1. 抽象主题(Subject):定义了真实对象和代理对象的共同接口,客户端通过该接口与真实对象进行交互。
  2. 真实主题(RealSubject):实现了抽象主题接口,是真正处理请求的对象。
  3. 代理(Proxy):实现了抽象主题接口,并持有一个对真实主题的引用。代理对象可以在调用真实主题的方法前后执行额外的操作。
1.3 代理模式的类型

代理模式根据用途不同,可以分为以下几种常见类型:

  • 静态代理(Static Proxy):代理类在编译时就已经确定,代理类与目标类实现相同的接口。
  • 动态代理(Dynamic Proxy) :代理类在运行时动态生成,通常使用 Java 的 java.lang.reflect.Proxy 或 CGLIB 库来实现。
  • 保护代理(Protection Proxy):控制对目标对象的访问权限。
  • 虚拟代理(Virtual Proxy):通过代理对象来延迟对真实对象的创建或初始化。
  • 缓存代理(Cache Proxy):通过代理对象在一定时间内缓存目标对象的结果,从而提升性能。

2. 代理模式的实现方式

2.1 静态代理

静态代理是一种在编译期就确定的代理模式。代理类需要实现与目标类相同的接口,客户端通过代理类调用目标对象的方法。静态代理通常用于在方法调用前后添加额外操作,比如日志记录、安全检查等。

示例代码

java 复制代码
// 抽象主题接口
public interface Service {
    void performAction();
}

// 真实主题类
public class RealService implements Service {
    @Override
    public void performAction() {
        System.out.println("执行真实操作");
    }
}

// 代理类
public class ServiceProxy implements Service {
    private RealService realService;

    public ServiceProxy() {
        this.realService = new RealService();
    }

    @Override
    public void performAction() {
        System.out.println("代理操作前的处理");
        realService.performAction();
        System.out.println("代理操作后的处理");
    }
}

// 测试客户端
public class Client {
    public static void main(String[] args) {
        Service service = new ServiceProxy();
        service.performAction();
    }
}

输出

java 复制代码
代理操作前的处理
执行真实操作
代理操作后的处理

在这个示例中,ServiceProxy 类在调用 RealService 的方法前后分别执行了额外的操作。静态代理的缺点是需要为每个业务对象编写代理类,导致代码量增加,灵活性差。

2.2 动态代理

动态代理是在运行时动态生成代理类,而不是在编译时生成。Java 提供了 java.lang.reflect.Proxy 类用于创建动态代理,它基于反射机制实现,因此代理类不需要实现接口。动态代理的优势是减少了重复的代理类代码,并且能够在运行时灵活地处理方法调用。

JDK 动态代理

示例代码

java 复制代码
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 抽象主题接口
public interface Payment {
    void pay(int amount);
}

// 真实主题类
public class RealPayment implements Payment {
    @Override
    public void pay(int amount) {
        System.out.println("支付金额:" + amount + " 元");
    }
}

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

    public PaymentInvocationHandler(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 Client {
    public static void main(String[] args) {
        Payment realPayment = new RealPayment();
        Payment proxyPayment = (Payment) Proxy.newProxyInstance(
                realPayment.getClass().getClassLoader(),
                realPayment.getClass().getInterfaces(),
                new PaymentInvocationHandler(realPayment)
        );
        proxyPayment.pay(100);
    }
}

输出

java 复制代码
支付前的安全检查
支付金额:100 元
支付后的日志记录

在这个示例中,动态代理类在运行时通过 Proxy.newProxyInstance() 方法动态创建。动态代理的优势在于它能够减少重复的代码,并在运行时灵活地处理不同的方法调用。

CGLIB 动态代理

JDK 动态代理只能代理实现了接口的类,而 CGLIB(Code Generation Library) 是一个开源的字节码生成库,它能够为没有实现接口的类创建动态代理。

示例代码

java 复制代码
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

// 目标类
public class BookService {
    public void addBook() {
        System.out.println("添加书籍");
    }
}

// CGLIB 动态代理类
public class CglibProxy implements MethodInterceptor {
    private Object target;

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

    public Object getProxyInstance() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("操作前的检查");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("操作后的日志");
        return result;
    }
}

// 测试客户端
public class Client {
    public static void main(String[] args) {
        BookService bookService = new BookService();
        CglibProxy proxy = new CglibProxy(bookService);
        BookService proxyService = (BookService) proxy.getProxyInstance();
        proxyService.addBook();
    }
}

输出

java 复制代码
操作前的检查
添加书籍
操作后的日志

CGLIB 通过继承目标类生成代理类,因此它适用于那些没有实现接口的类,但不能代理 final 类和 final 方法。

3. 代理模式的应用场景

  • 权限控制:通过代理对象控制对目标对象的访问权限。
  • 日志记录:在方法调用前后添加日志记录操作。
  • 事务管理:在方法调用前开启事务,调用后提交或回滚事务。
  • 缓存机制:通过代理对象在一定时间内缓存目标对象的结果,从而提升性能。
  • 延迟加载:代理对象延迟对真实对象的创建或加载,以节省资源。

4. 代理模式的优缺点

4.1 优点
  • 解耦:客户端无需关心真实对象的实现,只需依赖代理对象即可。
  • 增强功能:代理对象能够在调用真实对象的方法前后添加额外功能,而无需修改原有代码。
  • 灵活性:通过动态代理,能够在运行时动态添加功能,提高系统的灵活性。
4.2 缺点
  • 性能开销:由于代理模式增加了方法调用的中间层,可能会带来性能开销,尤其是动态代理和反射的使用。
  • 代码复杂度:引入代理模式会增加系统的复杂度,特别是在大量使用代理模式时,可能导致代码难以理解和维护。

5. 总结

代理模式是一种非常有用的设计模式,能够在不修改目标对象的情况下增强其功能。通过静态代理和动态代理的不同实现方式,可以根据具体场景选择最合适的代理模式。代理模式在实际开发中应用广泛,尤其是在权限控制、日志记录、缓存管理和事务处理等方面发挥了重要作用。理解和灵活应用代理模式,将有助于开发出更为灵活、可维护的系统。

相关推荐
吴冰_hogan4 分钟前
nacos集群源码解析-cp架构
java·spring boot·spring·架构·服务发现·springcloud
阿七想学习5 分钟前
数据结构《链表》
java·开发语言·数据结构·学习·链表
Yaml46 分钟前
Java的六大排序
java·算法·排序算法
XiaoLiuLB8 分钟前
Tomcat NIO 配置实操指南
java·tomcat·nio
Be_Somebody12 分钟前
[这可能是最好的Spring教程!]Maven的模块管理——如何拆分大项目并且用parent继承保证代码的简介性
java·spring boot·spring·spring入门
一个数据小开发28 分钟前
业务开发问题之ConcurrentHashMap
java·开发语言·高并发·map
会飞的架狗师44 分钟前
【Spring】Spring框架中有有哪些常见的设计模式
java·spring·设计模式
wclass-zhengge1 小时前
SpringCloud篇(服务拆分 / 远程调用 - 入门案例)
后端·spring·spring cloud
Jakarta EE1 小时前
在JPA和EJB中用乐观锁解决并发问题
java
花心蝴蝶.1 小时前
并发编程中常见的锁策略
java·jvm·windows