Java 动态代理
代理模式
代理模式的设计思想就是设置一个中间代理来控制访问原目标对象,以达到增强原对象的功能和简化访问方式。
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
按照代理的创建时期,代理类可以分为两种:
- 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译。(代理类在程序编译前就已经存在)
- 动态代理:代理类在程序运行时运用反射机制动态创建而成。(代理类运行时动态生成)
静态代理
静态代理实现
1 | // 定义接口 |
静态代理特点
- 优点:可以在不修改目标对象的前提下扩展目标对象的功能。
- 缺点:1,冗余。由于代理对象要实现与目标对象一致的接口,会产生过多的代理类;2,不易维护。一旦接口增加方法,目标对象与代理对象都要进行修改。
JDK 动态代理
JDK 动态代理的思路:在运行状态中,需要代理的地方,根据 Subject 和 RealSubject,动态地创建一个 Proxy,用完之后,就会销毁,这样就可以避免了 Proxy 角色的 class 在系统中冗杂的问题了。
InvocationHandler 与 Proxy
JDK 实现动态代理机制,需要用到 java.lang.reflect.InvocationHandler 和 java.lang.reflect.Proxy 类。
java.lang.reflect.InvocationHandler 接口只有一个方法 invoke:
1 | /* 参数说明: |
java.lang.reflect.Proxy 用来动态创建一个代理对象的类,我们主要使用 newProxyInstance 这个方法:
1 | /* 参数说明: |
JDK 动态代理实现
实现步骤:
- 实现 InvocationHanlder 接口,并重写 invoke 方法;
- 创建 InvocationHanlder 实例;
- 调用 Proxy.newProxyInstance() 返回一个代理对象;
- 使用代理对象。
1 | // 定义接口 |
通过 Proxy.newProxyInstance 创建的代理对象是在 jvm 运行时动态生成的一个对象,它并不是我们的 InvocationHandler 类型,也不是我们定义的那组接口的类型,而是在运行是动态生成的一个对象,其类名格式为 com.sun.proxy.$Proxy{num}。
JDK 动态代理特点
- 优点:相对于静态代理模式,不需要硬编码接口,代码复用率高。
- 缺点:只能代理接口。
CGLIB 动态代理
CGLIB(Code Generation Library)是一个基于 ASM 的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式为没有实现接口的类提供代理。
CGLIB作为一个开源项目,其代码托管在github,地址为:https://github.com/cglib/cglib
CGLIB 动态代理实现
实现步骤:
- 实现 MethodInterceptor 接口,并重写 intercept 方法;
- 创建 MethodInterceptor 实例。
- 创建 Enhancer 实例,并通过 setSuperclass 设置代理类,通过 setCallback 设置 MethodInterceptor 实例。
- 通过 enhancer.create 创建代理对象。
- 使用代理对象。
1 | // 接口的真实实现 |
CGLIB 动态代理特点
- 优点:使用字节码增强,比 JDK 动态代理方式性能高。可以在运行时对类或者是接口进行增强操作,且委托类无需实现接口。
- 缺点:不能对 final 类以及 final 方法进行代理。
参考文档
https://dunwu.github.io/javacore/basics/java-reflection.html#_4-2-jdk-%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86
https://juejin.cn/post/6844903983761326093
https://www.cnblogs.com/54chensongxia/p/12502624.html
https://blog.csdn.net/luanlouis/article/details/24589193
https://www.runoob.com/w3cnote/cglibcode-generation-library-intro.html