找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1563

积分

0

好友

231

主题
发表于 前天 01:00 | 查看: 8| 回复: 0

代理模式是一种常用的设计模式,它通过在客户端和目标对象之间引入一个中间层(即代理),来控制对目标对象的访问,有助于降低系统耦合度,并提高代码的扩展性和可维护性。

代理模式的基本概念

什么是代理模式?

简单来说,代理模式就是用一个代理对象来替代原始对象,从而控制对原始对象的访问。代理对象拥有与原始对象相同的接口,客户端与代理对象交互,而不直接访问原始对象。

这类似于现实中的经纪人与明星的关系:商业合作需通过经纪人进行,由经纪人处理洽谈、签约等前置与后置事宜。

代理模式的三种角色
  • 抽象主题角色:声明真实主题与代理的共同接口方法。
  • 真实主题角色:定义了代理所表示的真实对象,是负责执行业务逻辑的核心对象。
  • 代理主题角色:内部持有真实主题的引用,可以在调用真实主题方法前后添加控制或扩展功能。

静态代理:编译期确定的专属代理

工作原理

静态代理中,代理类与目标类的关系在编译期就已确定。需要手动编写一个实现了相同接口的代理类,并在其中调用目标方法,同时嵌入额外逻辑。

// 接口
public interface MsgService {
    void sendMsg(String message);
}

// 目标类
public class MsgServiceImpl implements MsgService {
    @Override
    public void sendMsg(String message) {
        System.out.println("发送短信:" + message);
    }
}

// 静态代理类
public class MsgProxy implements MsgService {
    private final MsgService msgService;

    public MsgProxy(MsgService msgService) {
        this.msgService = msgService;
    }

    @Override
    public void sendMsg(String message) {
        System.out.println("发送前进行预处理...");
        msgService.sendMsg(message);
        System.out.println("发送后进行后续处理...");
    }
}
优缺点与应用场景

优点:实现简单,易于理解和上手。
缺点:扩展性差。接口一旦增加方法,目标类和所有相关的代理类都需要同步修改并重新编译。

静态代理适用于代理逻辑固定且不常变化的场景,例如简单的日志记录、事务管理等。一个典型应用是数据源路由代理,它根据业务规则动态选择数据源:

public class OrderServiceStaticProxy implements IOrderService {
    private IOrderService orderService;

    public int createOrder(Order order) {
        before();
        // 根据订单创建时间选择数据源
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        DynamicDataSourceEntity.set(dbRouter);

        this.orderService.createOrder(order);
        DynamicDataSourceEntity.restore();

        after();
        return 0;
    }
}

这种代理就像一份“长期合同”,关系专一稳定,但缺乏应对变化的能力。

动态代理:运行期生成的灵活代理

核心思想

动态代理的代理类是在程序运行时动态生成的,无需手动编写代理类的源码。这使得我们可以用一个通用的处理器来代理多种不同类型的对象,极大地提升了灵活性。

JDK动态代理:基于接口

Java 原生提供了基于接口的JDK动态代理,核心是 java.lang.reflect.Proxy 类和 InvocationHandler 接口。

// 定义调用处理器
public class DynamicProxyInvokeHandler implements InvocationHandler {
    private final Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("调用方法之前:" + method.getName());
        Object result = method.invoke(target, args);
        System.out.println("调用方法之后:" + method.getName());
        return result;
    }
}

// 工厂方法创建代理
public class DynamicProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new DynamicProxyInvokeHandler(target)
        );
    }
}

限制:目标类必须实现至少一个接口。

CGLIB动态代理:基于继承

CGLIB是一个强大的第三方代码生成库,它通过继承目标类并重写方法的方式实现代理,因此不需要目标类实现接口

// CGLIB方法拦截器
public class MyMethodInterceptor implements MethodInterceptor {
    @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;
    }
}

// CGLIB代理工厂
public class CGLibProxyFactory {
    public static Object getProxy(Class<?> clazz) {
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new MyMethodInterceptor());
        return enhancer.create();
    }
}

限制:无法代理 final 类或 final 方法。

静态代理与动态代理的本质区别

从JVM层面看
  • 静态代理:在编译时,接口、实现类、代理类都已生成独立的 .class 文件。
  • 动态代理:在运行时,动态生成代理类的字节码,并加载到JVM中。可以类比为“纸质书”与“可更新电子书”的区别。
灵活性对比
  • 静态代理:一个代理类对应一个目标类,如果接口方法变动,所有相关类都需修改。
  • 动态代理:一个调用处理器(InvocationHandler)可以代理多个不同的目标类,所有方法调用都汇聚到 invoke 方法中统一处理,扩展性极强。
性能考量

静态代理在运行期直接调用,性能略高。动态代理首次调用因涉及类生成和加载,会有额外开销,但后续调用性能与静态代理相差无几。现代JVM对反射和动态代理的优化已使其性能损耗在大多数场景下可忽略不计。

实际应用场景深度解析

Spring框架中的代理机制

SpringBoot AOP的底层实现严重依赖于代理模式。Spring会根据目标Bean是否实现接口,智能选择JDK动态代理或CGLIB。

<!-- 在配置中可强制使用CGLIB代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>

这种机制使得面向切面编程(AOP)得以实现,将日志、事务等横切关注点与业务逻辑解耦。

MyBatis中的Mapper代理

MyBatis的Mapper接口正是JDK动态代理的经典应用。开发者只需定义接口,MyBatis在运行时通过 MapperProxyFactory 动态生成代理实现类,将接口方法调用转换为数据库操作。

RPC框架中的远程代理

远程过程调用(RPC)框架本质上是代理模式在分布式领域的延伸。客户端本地持有的是服务接口的代理对象,当调用其方法时,代理会拦截调用,将方法名、参数等信息序列化后通过网络发送给远程服务端,从而实现透明的远程调用。

设计模式与架构思想

代理模式与AOP编程

AOP的核心思想基于动态代理。通过代理,可以将横切关注点(如日志、性能监控、安全校验)模块化。例如,通过一个 InvocationHandler 统一为所有业务方法添加执行耗时统计,避免了代码重复。

对开闭原则的体现

代理模式完美体现了“对扩展开放,对修改关闭”的开闭原则。例如,可以通过创建一个缓存代理,在不修改原有业务类代码的情况下,为其方法增加缓存功能,提升系统性能。

public class CacheProxy implements InvocationHandler {
    private Object target;
    private Map<String, Object> cache = new HashMap<>();

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String cacheKey = generateCacheKey(method, args);
        if (cache.containsKey(cacheKey)) {
            return cache.get(cacheKey);
        }
        Object result = method.invoke(target, args);
        cache.put(cacheKey, result);
        return result;
    }
}

总结与展望

静态代理实现直观,适合简单、固定的增强场景;动态代理灵活强大,是构建高扩展性框架(如Spring、MyBatis)的基石。理解二者的区别,有助于我们在日常开发中做出更合适的技术选型。

随着云原生和微服务架构的发展,代理模式的思想在服务网格(Service Mesh)的边车(Sidecar)代理、API网关等基础设施中得到了更广泛和深入的应用,持续发挥着重要作用。




上一篇:RAG技术年终总结:从检索增强到上下文引擎的2025演进路径
下一篇:C# SIMD向量化编程实战:使用Vector<T>提升数值计算与图像处理性能
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 20:53 , Processed in 0.368154 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表