代理模式

使用代理对象来代替对真实对象的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

[实际应用很少] 静态代理

静态代理中,我们对目标对象的每个方法的增强都是手动完成的,非常不灵活(比如接口一旦新增加方法,目标对象和代理对象都要进行修改)且麻烦(需要对每个目标类都单独写一个代理类)。实际应用场景非常非常少,日常开发几乎看不到使用静态代理的场景。

静态代理实现步骤:

  • 定义一个接口及其实现类;
  • 创建一个代理类同样实现这个接口;
  • 将目标对象(接口实现类)注入代理类,然后在代理类的对应方法中调用目标类中的对应方法。

这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。

动态代理

动态代理是在运行时动态生成类字节码,并加载到 JVM 中的,不需要针对每个目标类都单独创建一个代理类并实现接口,比较灵活。

JDK 动态代理

核心是 InvocationHandler 接口和 Proxy 类。最大的问题是只能代理实现了接口的类。

Proxy 类使用频率最高的是 newProxyInstance() 方法,用于生成一个代理对象。

一共有三个角色:被代理的目标对象 target、代理对象,由 newProxyInstance 生成、InvocationHandler接口实现类,自定义(扩展)实现方法。
使用时,通过调用代理对象的目标对象方法,实际调用的是InvocationHandler接口实现类中的 invoke 方法。

// 生成代理对象
public static Object newProxyInstance(ClassLoader loader,    // 类加载器,用于加载代理对象
                                      Class<?>[] interfaces, // 
                                      InvocationHandler h) 
                     throws IllegalArgumentException {
        ......
}

// 使用
public static Object getProxy(Object target) { // target,被代理的目标对象
    return Proxy.newProxyInstance(
           target.getClass().getClassLoader(),  // 目标类的类加载器
           target.getClass().getInterfaces(),   // 代理需要实现的接口,可指定多个
           new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
    );
}

// 根据目标对象获取目标对象的代理对象
SmsService smsServiceProxy = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
// 方法调用,实际调用代理对象的 invoke 方法,在 invoke 方法中调用目标对象的对应方法
smsServiceProxy.send("java");

InvocationHandler 接口,核心是想要实现动态代理的话,需要实现 InvocationHandler 类来自定义代理逻辑。当动态代理对象 Proxy 调用一个方法时,这个方法的调用会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

CGLIB 动态代理

CGLIB 通过继承方式实现代理,很多知名的开源框架都使用到了 CGLIB,例如 Spring AOP 模块:如果目标对象实现了接口,则默认采用 JDK 动态代理,否则采用 CGLIB 动态代理。

核心是 MethodInterceptor 接口和 Enhancer 类。

CGLIB 动态代理类使用步骤:

  • 定义一个类;
  • 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  • 通过 Enhancer 类的 create()创建代理类;

JDK 动态代理与 CGLIB 动态代理的区别

参考资料:JavaGuide代理模式

posted @ 2025-06-24 16:21  千千菌  阅读(6)  评论(0)    收藏  举报