设计模式 - 静态代理 VS 动态代理

回到顶部(go to top)

QA

动态代理是什么?有哪些应用?

动态代理是运行时动态生成代理类。

动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpc,Java注解对象获取等。

 

怎么实现动态代理?

JDK 原生动态代理和 cglib 动态代理。

  • JDK 原生动态代理是基于接口实现的 
  • !!!重要的两个类!!!
    Proxy类
       Proxy.newProxyInstance 用来生成代理实例
    InvocationHandler接口
       要@Override invoke()方法,拦截所有原始类的各种方法,可在其前后增加逻辑

     

  • 而 cglib 是基于继承当前类的子类实现的
  • !!!重要的两个类!!!
    Enhancer类
       Enhancer.create(clazz, this) 获取被代理后的目标类
    MethodInterceptor接口
       要@Override intercept()方法,拦截所有原始对象的各种方法,可在其前后增加逻辑

     

回到顶部(go to top)

1-为什么需要代理?

当想给一个类的每个method进行扩充(统计、打印耗时)、拦截(判断是否登录)时:

常规思路

  • 修改了原来的代码逻辑
  • 每个涉及的method都需要改动,改动量很大

代理思路

可以使用代理(静态代理/动态代理),统一处理这类额外功能(非核心业务功能),让原来的代码仍然只关心最核心的业务逻辑。

 

回到顶部(go to top)

2-静态代理

关键点

代理类ServiceImplProxy和被代理类ServiceImpl实现同样的Service接口

代理类ServiceImplProxy持有被代理类ServiceImpl的引用

代理类ServiceImplProxy在接口方法中,填充扩展/拦截功能,核心逻辑依然由被代理类ServiceImpl的引用来完成

  

代码实例

接口 service

1
2
3
4
5
6
7
public interface Service {
  
  void play(String xxx);
   
  String run(String xxx);
   
}

 

被代理类 ServiceImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ServiceImpl implements Service{
 
    @Override
    public void play(String xxx){
    //....
    }
 
    @Override
    public String run(String xxx){
    //...
    }
 
}

 

代理类 ServiceImplProxy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class ServiceImplProxy implements Service{
 
    private Service service;
     
    //***关键*** 持有ServiceImpl的引用
    public ServiceImplProxy() {
        service = new ServiceImpl();
    }
 
    /**
    * 扩展功能
    */
    @Override
    public void play(String xxx){
        long start = System.currentTimeMillis();//计时开始
 
        //***关键*** 执行真正的逻辑
        service.play(xxx);
 
        long end = System.currentTimeMillis();//计时结束
        System.out.println("耗时:" + (end - start) + "毫秒");//打印耗时
 
    }
     
    /**
    * 拦截功能
    */
    @Override
    public String run(String xxx){
        //添加拦截逻辑
        if("xxx".equals("...")){
            //***关键*** 执行真正的逻辑
            service.run(xxx);
        }
        //...
    }
 
}

 

 

优缺点

优点:

  • 可以不修改原目标类(指ServiceImpl)的代码
  • 可以在代理类中对功能进行拦截和扩充

缺点:

  • 代理类需要实现与目标类一样的接口,会导致代理类数量较多,不易维护
  • 一旦接口增加方法,目标类和代理类都需要维护

鉴于这两个缺点,JDK 提供了动态代理

 

 

回到顶部(go to top)

3-动态代理,JDK(基于接口)

 

 

为何要引入JDK动态代理

为了解决静态代理的带来的以下问题 -> 引入了JDK动态代理:

  • 代理类需要实现与目标类一样的接口,会导致代理类数量较多,不易维护
  • 一旦接口增加方法,目标类和代理类都需要维护

 

代码示例

JDK动态代理的实现和静态代理一样,不同的是代理类的创建方式不同:

  • 静态代理是直接新增一个代理类。
  • JDK动态代理需要
    1. 持有目标类对象 - 代理类的通过构造方法传入目标类对象(会),代理类持有目标类对象 (第1步掌握“目标类的实例”,与第2步自动生成代理类无关(因为Proxy.newProxyInstance只需传入clazz,不需要实例),与第3步反射调用方法有关(因为哪怕增加别的逻辑,但核心逻辑依然要调用“目标类的实例”)
    2. 创建代理对象 - 通过JDK的Proxy类, Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法获取代理对象, 和一个"调用处理器InvocationHandler"来实现的,通过Proxy来生成代理类实例,而这个代理实例通过调用处理器InvocationHandler的invoke()接收不同的参数, 反射调用真实对象的方法。
    3. 代理类实现 InvocationHandler 接口,重写invoke(Object proxy, Method method, Object[] args)方法。在此需要反射调用第1步传入的“目标类的实例”method.invoke(“目标类的实例”, args),并在此周围添加更多的逻辑,如过滤器等。

1-实现JDK动态代理的util代码(拷贝到任何项目中,都能直接使用) 

!!!重要的两个类!!!
Proxy类
   Proxy.newProxyInstance 用来生成代理实例
InvocationHandler接口
   要@Override invoke()方法,拦截所有原始类的各种方法,可在其前后增加逻辑

 

 

2-实际例子

 

 

   

 

优缺点

优点:见“为何要引入JDK动态代理”

缺点:只能对该类,所实现接口中定义的方法,进行代理。 

 

详细解析

 

 

回到顶部(go to top)

4-动态代理,CGlib(基于类)

为何要引入CGlib动态代理

为了解决 JDK 的动态代理无法代理不实现接口的类的问题 -> 引入使用 CGLib 的实现动态代理。

CGLib(Code Generator Library)是一个强大的、高性能的代码生成库。底层使用了ASM(一个短小精悍的字节码操作框架)来操作字节码生成新的类。动态生成一个目标类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

 

关键点

  • CGlib动态代理需要
    • maven中添加cglib依赖
    • 不再持有目标类对象 - 构造函数就是默认的空构造函数
    • 创建代理对象 - 使用cglib自带的Enhancer.create(clazz), 参数是目标类的clazz对象。Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。
    • 定义拦截器/处理器 - 在调用目标方法时,CGLib会回调MethodInterceptor(Object obj, Method method, Object[] params, MethodProxy proxy)接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口
      • 参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。
      • 返回:从代理实例的方法调用返回的值。
      • 其中,proxy.invokeSuper(obj,arg) 调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

 

优缺点

优点:完全不受代理类必须实现接口的限制

缺点:对于final方法,无法进行代理

 

详细解析

CGLib 动态代理 https://blog.csdn.net/meism5/article/details/90781518

CGLIB(Code Generation Library) 介绍与原理 https://www.runoob.com/w3cnote/cglibcode-generation-library-intro.html

 

posted on   frank_cui  阅读(226)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

导航

统计

levels of contents
点击右上角即可分享
微信分享提示