`
flyingdutchman
  • 浏览: 353342 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

设计模式:代理模式之动态代理

阅读更多
        代理是一种常用的设计模式,其目的是为其他对象提供一种代理以控制(外部对象)对这个被被代理对象的的访问。由代理类负责为委托类(即被代理类)对象做一些用户处理的操作(如权限限制)或执行完之后的后续操作。
        代理模式的特征是:代理类与委托类(即被代理类)有同样的接口,代理类主要负责为委托类(即被代理类)预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
        线面我们来看一下代理模式的类图:

        我们这里主要学习以下动态代理模式,动态代理是在运行时,运用反射机制动态创建代理类。
        常用的动态代理分为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方式要更为合适一些。
  • 大小: 55 KB
分享到:
评论
1 楼 wisesean 2013-06-03  
好文章 一看就懂了

相关推荐

Global site tag (gtag.js) - Google Analytics