代理模式的特征是:代理类与委托类(即被代理类)有同样的接口,代理类主要负责为委托类(即被代理类)预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
线面我们来看一下代理模式的类图:
我们这里主要学习以下动态代理模式,动态代理是在运行时,运用反射机制动态创建代理类。
常用的动态代理分为JDK动态代理和CGLIB动态代理。对于这两种动态代理, JDK的动态代理需要依靠接口实现,如果有些类并没有实现接口,则不能使用JDK动态代理;而CGLIB是针对类来实现代理的,其原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,故不能对final修饰的类进行代理。
就我们熟知的Spring框架中的AOP默认就是使用JDK动态代理的,如果想强制使用CGLIB实现代理,那就需要添加CGLIB库,并在Spring配置中加入:
<aop:aspectj-autoproxy proxy-target-class="true"/>
就性能而言,CGLIB动态代理要较JDK的K动态代理高一些。
JDK动态代理
在JDK的动态代理中,除了要实现的接口外,外特别关注java.lang.reflect包下的一个类Proxy和动态代理要实现的接口InvocationHandler。
Proxy是Java动态代理的主类,它提供了一些静态方法,用于为一组儿接口动态地生成代理类及其对象:
//获取指定的代理对象关联的调用处理器 static InvocationHandler getInvocationHandler(Object proxy) //获取关联于指定类加载器和一组儿接口的动态代理类的对象 static Class getProxyClass(ClassLoader loader,Class[] interfaces) //判断指定的类是否是一个动态代理类 static boolan isProxyClass(Class cl) /** * 非常重要的接口:用于为指定的类加载器、一组儿接口和嗲用处理器生成 * 动态代理累的实例 */ static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)
再来看一下InvocationHandler接口,它主要定义了一个invoke方法,用于处理动态代理类对象上的方法调用。通常开发者需要实现该接口并在其方法内实现对委托类(被代理类)对象的代理访问:
/** * 该方法负责处理动态代理类上的方法调用。 * @Param proxy: 代理类的实例 * @Param method: 被调用的方法对象 * @Param args: 调用参数 */ Object invoke(Object proxy,Method method,Object[] args)
具体代码如下:
package org.pattern; /** * 卖货接口 * @author Jacky.Chen * */ public interface ISell { /** * 卖货 */ void sell(); }
package org.pattern; /** * 销售商,实现了ISell接口 * @author JackyChen * */ public class Vendor implements ISell{ @Override public void sell(){ System.out.println("今天,俺又卖了一件货品!"); } }
package org.pattern; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 销售商代理,比如说淘宝上的一个代理商,现有该代理商 * 在网上接到客户的订单,然后他(她)再向销售商Vendor * 下单赚区差价 * @author JackyChen * */ public class VendorProxyHandler implements InvocationHandler { private Object objOriginal; public VendorProxyHandler(Object obj){ this.objOriginal = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //可以在这里添加一些预处理操作,如权限控制等... Object result = method.invoke(this.objOriginal, args); //也可以在此处添加一些后置处理操作... return result; } }
package org.pattern; import java.lang.reflect.Proxy; public class JDKDynamicProxyExample{ public static void main(String[] args){ //创建销售商 Vendor vendor = new Vendor(); //创建动态代理的处理器 VendorProxyHandler handler = new VendorProxyHandler(vendor); //创建动态代理类 ISell proxyObj = (ISell) Proxy.newProxyInstance(vendor.getClass().getClassLoader(), Vendor.class.getInterfaces(), handler); //卖货 proxyObj.sell(); } }
CGLIB动态代理
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
下面我们来看一下CGLIB动态代理中涉及的几个类:
net.sf.cglib.proxy.Enhancer – 主要的增强类
net.sf.cglib.proxy.MethodInterceptor – 主要的方法拦截类,它是Callback接口的子接口,需要用户实现
net.sf.cglib.proxy.MethodProxy – JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用,如使用:
Object o = methodProxy.invokeSuper(proxy, args);
我们在这里简单举例,首先要有一个被代理的类,该类不能是final类:
package org.pattern; /** * 需要被代理的类(也就是父类),通过字节码技术创建这个类的子类,实现动态代理 * @author JackyChen * */ public class CglibVendor { public void sell(){ System.out.println("今天,俺又卖了一件货品!"); } }
接下来我们来实现代理类:
package org.pattern; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * Cglib代理类 * @author JackyChen * */ public class CglibVendorProxy implements MethodInterceptor{ private Enhancer enhancer = new Enhancer(); public Object getProxy(Class clazz){ /设置需要创建子类的类 enhancer.setSuperclass(clazz); enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } /** * 拦截所有目标类方法的调用 * @param obj: 目标类的实例 * @param method: 目标类方法的反射对象 * @param args: 方法的动态入参 * @param proxy: 代理类实例 */ @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { //可以在这里添加一些预处理操作,如权限控制等... //通过代理类调用父类中的方法 Object result = proxy.invokeSuper(obj, args); //也可以在此处添加一些后置处理操作... return result; } }
调用Cglib动态代理的示例:
package org.pattern; public class CglibDynamicProxyExample { public static void main(String[] args){ CglibVendorProxy proxy = new CglibVendorProxy(); //通过生成子类的方式创建代理类 CglibVendor proxyImp = (CglibVendor)proxy.getProxy(CglibVendor.class); proxyImp.sell(); } }
从上面的代码量上来看,Cglib动态代理要少很多,并且代码较为清晰而干净。在性能上: CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。