代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种。
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
静态代理的每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
动态代理
与静态代理类对照的是动态代理类,动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性,因为Java 反射机制可以生成任意类型的动态代理类。java.lang.reflect 包中的Proxy类和InvocationHandler 接口提供了生成动态代理类的能力。
Spring AOP使用动态代理技术在运行期间织入增强的代码,主要有两种代理机制:基于JDK的动态代理;基于CGLib的动态代理。JDK本身只提供接口的代理,而不支持类的代理。
一.JDK动态代理
JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy类和InvocationHandler接口。
InvocationHandler接口:
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
参数说明:
Object proxy:指被代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
InvocationHandler接口的子类可以看成代理的最终操作类。
Proxy类:
Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
参数说明:
ClassLoader loader:类加载器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子类实例
JDK动态代理主要涉及java.lang.reflect包下的两个类:Proxy和InvocationHandler。其中,InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起。Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。
代码:移除性能监视横切代码
public class ForumServiceImpl implements ForumService{ public void removeTopic(int topicId){ //①在此位置原有的横切代码被移除(抽取切面中) System.out.println("模拟删除Topic记录:"+topicId); try{ Thread.currentThread().sleep(20); }catch(Exception e){ throw new RuntimeException(e); } //①在此位置原有的横切代码被移除(抽取切面中) } public void removeForum(int forumId){ //②在此位置原有的横切代码被移除(抽取切面中) System.out.println("模拟删除Forum记录:"+forumId); try{ Thread.currentThread().sleep(40); }catch(Exception e){ throw new RuntimeException(e); } //②在此位置原有的横切代码被移除(抽取切面中) } }
把抽取出来的代码性能监视代码放置到PerformanceHandler中:
public class PerformanceHandler implements InvocationHandler{ private Object target; //target为目标业务类 public PerformanceHandler(Object target){ this.target=target; } public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ PerformanceMonitor.begin(target.getClass().getName()+"."+method.getName()); //③ Object obj=method.invoke(target, args); //⑤通过反射调用业务类的目标方法 PerformanceMonitor.end(); //④ return obj; } }
invoke()方法中③④处的代码为性能监视的横切代码,只出现一次。⑤处的method.invoke()语句通过java反射机制间接调用目标对象的方法,这样InvocationHandler的invoke()方法就将横切逻辑代码和业务类方法的业务逻辑代码编织到一起,我们可以把InvocationHandler看成一个编织器。
我们通过Proxy和PerformanceHandler创建ForumService接口的代理实例:
import java.lang.reflect.Proxy; public class TestForumService { public static void main(String[] args){ ForumService target=new ForumServiceImpl(); //被代理对象 PerformanceHandler handler=new PerformanceHandler(target); //将目标业务类和横切代码编织到一起 ForumService proxy=(ForumService)Proxy.newProxyInstance( //创建代理实例 target.getClass().getClassLoader, target.getClass().getInstances(), handler); proxy.removeForum(10); proxy.removeTopic(1012); } }
但是,JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
二.CGLib动态代理
CGLib采用非常底层的字节码技术,可以为一个类创建子类,并在子类中采用方法拦截的技术拦截所有的父类方法的调用,并顺势织入横切逻辑。
下面采用CGLib技术写一个可以为任何类创建织入性能监视横切逻辑代理对象的代理创建器:
import class CglibProxy implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ enhancer.setSuperclass(clazz); //设置需要创建子类的类 enhancer.setCallback(this); return enhancer.create(); //通过字节码技术动态创建子类实例 } public Object intercept(Object obj,Method method,Object[] args,MethodProxy proxy) throws Throwable{//拦截父类所有方法的调用 PerformanceMonitor.begin(obj.getClass().getName()+"."+method.getName()); //① Object result = proxy.invokeSuper(obj,args); //通过代理类调用父类中的方法 PerformanceMonitor.end(); //② return result; } }
getProxy(Class clazz):为一个类创建动态代理对象,该代理对象通过扩展clazz创建代理对象。在这个代理对象中,我们织入性能监视的横切逻辑①②。
intercept(Object obj,Method method,Object[] args,MethodProxy proxy):是CGLib定义的Intercept接口的方法,它拦截所有目标类方法的调用,obj:目标类的实例;method:目标类方法的反射对象;args:方法的动态入参;proxy:代理类实例。
下面通过CglibProxy为ForumServiceImpl类创建代理对象,并测试代理对象的方法:
import java.lang.reflect.Proxy; public class TestForumService{ public static void main(String[] args){ CglibProxy proxy = new CglibProxy(); ForumServiceImpl forumService = (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class); //动态生成子类的方式创建代理类 forumService.removeForum(10); forumService.removeTopic(1023); } }