JDK动态代理涉及java.lang.reflect包中的两个类,Proxy和InvocationHandler。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。InvocationHandler可以实现接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
JDK动态代理
下面摘自:《疯狂java讲义 第四版》 p857
当程序使用反射方式为指定接口生成系列动态代理对象时,这些动态代理对象的实现类实现了一个或多个接口。动态代理对象就需要实现一个或多个接口里定义的所有方法,但问题是:系统怎么知道如何实现这些方法? 这个时候就轮到 InvocationHandler 对象登场了,当执行动态代理对象里的方法时,实际上会替换成调用 InvocationHandler 对象的 invoke 方法。
在 Java 中,动态代理类的生成主要涉及对 ClassLoader 的使用。以 CGLIB 为例,使用 CGLIB 生成动态代理,首先需要生成 Enhancer 类实例,并指定用于处理代理业务的回调类。在 Enhancer.create() 方法中,会使用 DefaultGeneratorStrategy.Generate() 方法生成动态代理类的字节码,并保存在 byte 数组中。接着使用 ReflectUtils.defineClass() 方法,通过反射,调用 ClassLoader.defineClass() 方法,将字节码装载到 ClassLoader 中,完成类的加载。最后使用 ReflectUtils.newInstance() 方法,通过反射,生成动态类的实例,并返回该实例。基本流程是根据指定的回调类生成 Class 字节码—通过 defineClass() 将字节码定义为类—使用反射机制生成该类的实例。
程序中可以采用先生成一个动态代理类,然后通过动态代理类来创建代理对象的方式生成一个动态代理对象。代码如下:
1 | // 创建一个InvocationHandler对象 |
上面的代码也可以简化成如下代码:
1 | // 创建一个InvocationHandler对象 |
下面的代码来自于《精通Spring 4.x企业应用开发实战》 P224
业务逻辑横切代码
业务逻辑实现类的代码,省去ForumService接口类和PerformanceMonitor的代码。
1 | public class ForumServiceImpl implements ForumService { |
横切逻辑
将业务类中性能监视横切代码移除后,放置到InvocationHandler中,代码如下。
1 | import java.lang.reflect.Method; |
invoke(Object proxy, Method method, Object[] args)方法,其中,proxy是最终生成的代理实例,一般不会用到;method是被代理目标实例的某个具体方法,通过它可以发起目标实例方法的反射调用;args是被代理实例某个方法的入参,在方法反射时调用。
其次,在构造参数里通过target传入希望被代理的目标对象,在接口方法invoke(Object proxy, Method method, Object[] args)里,将目标实例传递给method.invoke()方法,并调用目标实例的方法。
proxy代表动态代理对象,method代表正在执行的方法,args代表调用目标方法是传入的实参。
下面通过Proxy结合PerformanceHandler创建ForumService接口的代理实例。
创建代理实例
1 | import java.lang.reflect.Proxy; |
Proxy.newProxyInstance() 方法的第一个入参为类加载器;第二个入参为创建代理实例所需实现的一组接口;第三个入参是整合了业务逻辑和横切逻辑的编织器对象。
JDK动态代理另一种简洁写法
以下代码来自于 Java EE 互联网轻量级框架整合开发
接口类和实现类的定义
在动态代理中必须使用接口,CGLib不需要。
下面的代码分别是简单的接口和被代理类的定义。
1 | // 接口 |
动态代理绑定和代理逻辑实现
要实现动态代理要两个步骤,首先,建立起代理对象和被代理对象的关系(将目标业务类和横切代码编织到一起),然后实现代理逻辑。
1 | import java.lang.reflect.Proxy; |
测试动态代理类
1 | public class JdkProxyExampleTest { |
bind方法同时完成了两步。
CGLib动态代理
使用JDK创建代理有一个限制,即只能为接口创建代理。Proxy的接口方法中newProxyInstance(ClassLoader loder, Class[] interfaces, InvocationHandler hander),第二个入参就是需要代理实例实现的接口列表。
假如对一个简单业务表的操作也需要创建5个类(领域对象、DAO接口、DAO实现类、Service接口和Service实现类)吗?
对于没有通过接口定义业务方法的类,可以使用CGLib动态创建代理实例。
CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中采用方法拦截的技术拦截父类方法的调用并顺势织入横切逻辑。
值得一提的是,由于CGLib采用动态创建子类的方式生成代理对象,所以不能对目标类中的final或private方法进行代理。
CglibProxy
下面代码可以创建,为任何类织入性能监视横切逻辑代理对象的代理创建器。
1 | import java.lang.reflect.Method; |
用户可以通过getProxy(Class cla)方法动态创建一个动态代理类。
Cglib测试代码
1 | import java.lang.reflect.Proxy; |
拦截器
拦截器接口
1 | public interface Interceptor { |
Interceptor实现类
1 | public class MyInterceptor implements Interceptor { |
在JDK动态代理中使用拦截器
1 | public class InterceptorJdkProxy implements InvocationHandler { |