JDK动态代理机制
代理模式
代理模式简单来说就是,使用代理对象代替真实对象的访问。可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能,比如在某个方法执行前后,可以增加一些自定义的操作。
[静态代理](多线程详解.md/#静态代理模式 static proxy)
动态代理
静态代理中,对目标对象的每个方法的扩展都是手动完成的,非常不灵活(比如一旦新增方法,目标对象和代理对象都要进行修改)、麻烦(需要对每个目标类都单独写一个代理类)。日常开发几乎看不到使用静态代理的场景。
相比于静态代理来说,动态代理更加灵活。不需要针对每个目标类都单独创建一个代理类,并且也不需要我们必须实现接口,可以直接代理实现类(CGLIB 动态代理机制)。从JVM角度说,动态代理是在运行时 动态生成类字节码,并加载到JVM中。
Spring AOP、RPC框架的实现都依赖了动态代理。动态代理在日常开发中使用相对较少,但在框架中是必用的一门技术,对于各种框架原理的理解和学习很有帮助。就Java来说,动态代理的实现方式有很多种,比如JDK 动态代理、CGLIB 动态代理。
JDK 动态代理机制
InvocationHandler
接口和Proxy
类是核心。
Proxy.newProxyInstance
,该方法主要用来生成一个代理对象。通过Proxy类的newProxyInstance()
方法创建的代理对象,在调用方法的时候,实际会调用到实现InvocationHandler
接口的类的invoke()
方法。可以在invoke()
方法种自定义处理逻辑,比如在方法执行前后做什么事情。
使用步骤
- 定义一个接口及其实现类。
- 实现
InvocationHandler
接口并重写invoke方法,在其中调用被代理类的方法,并自定义一些处理逻辑。 - 通过
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
方法创建代理对象。ClassLoader loader
:指定用哪个类加载器来加载代理类。Class<?>[] interfaces
:指定代理类需要实现的接口(返回的对象可以被安全地转型为实现了这个接口的类型)InvocationHandler h
:指定第2步创建的调用处理器
示例
定义一个发送短信的接口及其实现类
1 | public interface SmsService { |
实现InvocationHandler接口并重写invoke方法
1 | public class JdkInvocationHandler implements InvocationHandler { |
通过工厂类创建代理对象
1 | public class JdkProxyFactory { |
实际使用
1 | public class Main { |
CGLIB 动态代理机制
JDK 动态代理有一个最致命的问题:只能代理实现了接口的类。CGLIB 动态代理机制可以避免这个问题。
CGLIB(Code Generation Library) 是一个字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。Spring中的AOP模块,如果目标对象实现了接口,则默认采用JDK动态代理,否则采用CGLIB动态代理。
在CGLIB动态代理机制中,MethodInterceptor
接口和Enhancer
类是核心。
使用步骤
- 定义一个类
- 实现
MethodInterceptor
接口并重写intercept
方法,intercept
用于拦截扩展被代理类的方法,和 JDK 动态代理中的invoke
方法类似。 - 通过
Enhancer
类的create()
方法创建代理类。
1 | <!-- CGLIB 动态代理 --> |
示例
定义一个发送短信的类
1 | public class SmsService { |
实现方法拦截器MethodInterceptor
1 | public class CglibMethodInterceptor implements MethodInterceptor { |
通过工厂类创建代理对象
1 | public class CglibProxyFactory { |
实际使用
1 | public class Main { |
JDK 动态代理 和 CGLIB 动态代理的对比
- JDK动态代理只能代理实现了接口的类,CGLIB可以代理未实现任何接口的类。另外,CGLIB动态代理是通过生成一个被代理的子类,来拦截被代理类的调用,因此不能代理声明为final类型的类和方法。
- JDK 动态代理的效率更高
静态代理和动态代理的对比
灵活性
动态代理更加灵活,不需要实现接口,可以直接代理实现类。静态代理中,接口一旦新增方法,目标对象和代理对象都需要修改,非常麻烦
JVM层面
静态代理在编译时,就将接口、实现类、代理类这些都变成了实际的class文件;动态代理是在运行时动态生成类字节码,并加载到JVM中。