Java代理机制
Java代理机制
Java代理分为静态代理与动态代理。
Java提供了两种主要类型的代理:静态代理和动态代理。动态代理是在运行时生成代理对象,通常基于接口,而静态代理是在编译时手动编写代理类。
静态代理就不说了,可以去本站的文章 Spring:AOP简介查看。
从JVM角度来说 :
- 静态代理是在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。
- 动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。
就 Java 来说,动态代理的实现方式有很多种,比如 JDK动态代理 、 CGLIB动态代理等。
由于miniSpring中使用JDK动态代理实现,这里详细介绍JDK动态代理 .
JDK动态代理
在 Java 动态代理机制中 InvocationHandler
接口和 Proxy
类是核心。
以计算器为例,这样一个计算机接口 :
1 | public interface Calculator { |
其实现为 :
1 | public class CalculatorImpl implements Calculator { |
目标:对CalculatorImpl形成代理,让其能够打印日志。
生成动态代理对象:newProxyInstance()
生成一个代理对象:使用Proxy
类中使用频率最高的方法:newProxyInstance()
1 | /** |
当我们对一个接口的实现类做代理时,参数 loader 和interfaces就有了。
即
1 | Calculator calculator = new CalculatorImpl();//对Calculator做代理 |
还缺 handler
参数 。
自定义处理逻辑:InvocationHandler
要实现动态代理的话,还必须需要实现InvocationHandler
来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler
接口类的 invoke
方法来调用。
也就是说:你通过Proxy
类的 newProxyInstance()
创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的 invoke()
方法。 你可以在 invoke()
方法中自定义处理逻辑,比如在方法执行前后做什么事情。
1 | public interface InvocationHandler { |
总体流程
1. 创建接口: 首先,你需要定义一个接口,该接口包含要被代理的方法。例如,假设你有一个简单的计算器接口如下:
1 | javaCopy codepublic interface Calculator { |
2. 创建目标对象: 接下来,你需要创建一个实现了上述接口的目标对象,即真正执行业务逻辑的对象。
1 | javaCopy codepublic class CalculatorImpl implements Calculator { |
3. 创建 InvocationHandler: 你需要实现 InvocationHandler
接口,该接口包含一个方法 invoke
,在代理对象调用方法时会被触发。在 invoke
方法中,你可以添加额外的逻辑,例如日志记录、权限检查等。
1 | javaCopy codeimport java.lang.reflect.InvocationHandler; |
4. 创建代理对象: 使用 java.lang.reflect.Proxy
类的 newProxyInstance
方法创建代理对象。你需要传入一个类加载器、一个接口数组和一个 InvocationHandler
对象。
1 | javaCopy codeimport java.lang.reflect.Proxy; |
在上述示例中,proxy
对象是一个动态生成的代理对象,它实现了 Calculator
接口并拦截了 add
方法的调用,执行了 CalculatorInvocationHandler
中定义的额外逻辑。这种方式允许你在不修改原始 CalculatorImpl
类的情况下,添加日志记录、权限检查等功能。
CGLIB动态代理
JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。为了解决这个问题,我们可以用 CGLIB 动态代理机制来避免。
拦截方法与获取代理类
在 CGLIB 动态代理机制中 MethodInterceptor
接口和 Enhancer
类是核心。
你需要自定义
MethodInterceptor
并重写intercept
方法,intercept
用于拦截增强被代理类的方法。可以通过
Enhancer
类来动态获取被代理类,当代理类调用方法的时候,实际调用的是MethodInterceptor
中的intercept
方法。
1 | public interface MethodInterceptor extends Callback{ |
总体流程
1. 添加依赖: 首先,你需要添加CGLIB库的依赖。通常,你可以使用Maven或Gradle来添加CGLIB的依赖。例如,使用Maven:
1 | <dependency> |
2.创建目标对象: 与JDK动态代理不同,CGLIB可以代理没有实现接口的类。因此,你需要创建一个目标类,该类将成为代理的目标。
1 | class MyService { |
3. 创建CGLIB代理: 使用CGLIB,你可以创建一个代理对象来拦截目标对象的方法调用。
1 | import net.sf.cglib.proxy.Enhancer; |
在上述代码中,我们创建了一个 CglibProxy
类,该类实现了 MethodInterceptor
接口,用于拦截方法调用。在 intercept
方法中,我们可以在方法调用前后添加额外的逻辑。
在 createProxy
方法中,我们使用 Enhancer
类创建代理对象,并指定目标类的类类型。
4. 使用CGLIB代理: 最后,我们可以使用CGLIB代理来代替目标对象。
1 | public class CglibProxyExample { |
在上述示例中,我们创建了 CglibProxy
对象,并使用它来创建 MyService
类的代理对象。当我们调用代理对象的 doSomething()
方法时,代理会在方法执行前后输出日志。
JDK vs CGLIB
- JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
- 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。
- JDK动态代理: 不需要额外的依赖,因为它是Java标准库的一部分。CGLIB动态代理: 需要添加CGLIB库的依赖,通常使用Maven或Gradle进行依赖管理。