JDK动态代理涉及java.lang.reflect包中的两个类,Proxy和InvocationHandler。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。InvocationHandler可以实现接口定义横切逻辑,并通过反射机制调用目标类的代码,动态地将横切逻辑和业务逻辑编织在一起。
JDK动态代理
下面的代码来自于 精通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结合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 { |