代理模式深入

http://blog.csdn.net/zhuojiajin/article/details/38531539

故事

    周末放假,小孙睡到12点才告别周公醒来,顿时饥肠辘辘。舍长小王正准备去食堂买饭,作为一个好舍长小王主动要帮小孙带饭。小孙点了米饭、宫保鸡丁、芬达。小孙起床洗漱,然后静待舍长。小孙心理寻思道舍长果然是好舍长啊。下面我们先把这个故事抽象一下,画作类图。这个类图即代理模式。

代理模式

   定义:为其他对象提供一种代理以控制对这个对象的访问。怎么理解这句话呢?从生活的角度来说就是:用一个对象A代替另一个对象B来处理本来应该由对象B完成的事情,例如上面买饭的例子,又如通过黄牛买票等等。从代码的角度来说则是:用一个对象A来过滤、预处理对对象B的调用 。不管从哪个角度看吧,代理都需要拥有和实际对象的需要被代理的全部方法。

静态代理

    类图如上所示了,定义也看了。接下来我们按照类图来看看代码。

    接口(宿舍成员)

[java] view plaincopyprint?

  1. /** 
  2.  * 宿舍成员接口 
  3.  * 开发时间:2014-8-12 下午9:17:19 
  4.  */  
  5. public interface DormitoryMember {  
  6.     //买主食   
  7.     public boolean buyStapleFood(String stapleFoodName);  
  8.     //买菜   
  9.     public boolean buyDish(String dishName);  
  10.     //买饮料   
  11.     public boolean buyDrink(String drinkName);     
  12. }  
/**
 * 宿舍成员接口
 * 开发时间:2014-8-12 下午9:17:19
 */
public interface DormitoryMember {
	//买主食
    public boolean buyStapleFood(String stapleFoodName);
    //买菜
    public boolean buyDish(String dishName);
    //买饮料
    public boolean buyDrink(String drinkName);
}

    代理(宿舍长)    

[java] view plaincopyprint?

  1. //代理类   
  2. public class DormitoryMemberProxy implements DormitoryMember {  
  3.     //持有一个被代理的对象   
  4.     private DormitoryMember dormitoryMember;  
  5.     //构造时传入被代理对象   
  6.     public DormitoryMemberProxy(DormitoryMember dormitoryMember){  
  7.         this.dormitoryMember=dormitoryMember;  
  8.     }  
  9.     //代理买主食   
  10.     public boolean buyStapleFood(String stapleFoodName) {  
  11.         return dormitoryMember.buyStapleFood(stapleFoodName);  
  12.     }  
  13.     //代理买菜   
  14.     public boolean buyDish(String dishName) {  
  15.         return dormitoryMember.buyDish(dishName);  
  16.     }  
  17.     //代理买饮料   
  18.     public boolean buyDrink(String drinkName) {  
  19.         return dormitoryMember.buyDrink(drinkName);  
  20.     }  
  21. }  
//代理类
public class DormitoryMemberProxy implements DormitoryMember {
	//持有一个被代理的对象
    private DormitoryMember dormitoryMember;
    //构造时传入被代理对象
    public DormitoryMemberProxy(DormitoryMember dormitoryMember){
    	this.dormitoryMember=dormitoryMember;
    }
    //代理买主食
	public boolean buyStapleFood(String stapleFoodName) {
		return dormitoryMember.buyStapleFood(stapleFoodName);
	}
    //代理买菜
	public boolean buyDish(String dishName) {
		return dormitoryMember.buyDish(dishName);
	}
    //代理买饮料
	public boolean buyDrink(String drinkName) {
		return dormitoryMember.buyDrink(drinkName);
	}
}

     实际类(小孙)

[java] view plaincopyprint?

  1. //实际的类   
  2. public class DormitoryMemberImpl implements DormitoryMember {  
  3.     //买主食   
  4.     public boolean buyStapleFood(String stapleFoodName) {  
  5.         try{  
  6.             System.out.println("买到主食" + stapleFoodName);  
  7.         }catch(Exception e){  
  8.             return false;  
  9.         }  
  10.         return true;  
  11.     }  
  12.     //买菜   
  13.     public boolean buyDish(String dishName) {  
  14.         try{  
  15.             System.out.println("买到菜:" + dishName);  
  16.         }catch(Exception e){  
  17.             return false;  
  18.         }  
  19.         return true;  
  20.     }  
  21.     //买饮料   
  22.     public boolean buyDrink(String drinkName) {  
  23.         try{  
  24.             System.out.println("买到饮料:" + drinkName);  
  25.         }catch(Exception e){  
  26.             return false;  
  27.         }  
  28.         return true;  
  29.     }  
  30. }  
//实际的类
public class DormitoryMemberImpl implements DormitoryMember {
    //买主食
	public boolean buyStapleFood(String stapleFoodName) {
		try{
			System.out.println("买到主食" + stapleFoodName);
		}catch(Exception e){
			return false;
		}
		return true;
	}
    //买菜
	public boolean buyDish(String dishName) {
		try{
			System.out.println("买到菜:" + dishName);
		}catch(Exception e){
			return false;
		}
		return true;
	}
    //买饮料
	public boolean buyDrink(String drinkName) {
		try{
			System.out.println("买到饮料:" + drinkName);
		}catch(Exception e){
			return false;
		}
		return true;
	}
}

故事继续

    宿舍长去到食堂买饭,结果小孙要的芬达断货了。于是宿舍长久提着米饭和菜就回去了,小孙吃着米饭和菜没有饮料难以下咽。但是,也不好说宿舍长,舍长是好舍长啊。将就的吃了午饭。时间来的晚饭时间,小孙在英雄联盟里正杀的起劲,那又闲工夫去买饭于是这工作有落到了好舍长身上。这次小孙学聪明了交代舍长说,如果有他点的东西没有了就打个电话回来在决定买什么替代。

   问题来了

    这里需求已经改变了,如果小孙要的东西没有了的话,要给小孙打电话以决定是不是买其他的或者不买。这个电话自然是宿舍长来打。所以我们要对代理类进行修改!这也就是使用代理模式的好处,我们关闭了对实际类的修改。代理类修改后的代码如下:

[java] view plaincopyprint?

  1. //代理类   
  2. public class DormitoryMemberProxy implements DormitoryMember {  
  3.     //持有一个被代理的对象   
  4.     private DormitoryMember dormitoryMember;  
  5.     //构造时传入被代理对象   
  6.     public DormitoryMemberProxy(DormitoryMember dormitoryMember){  
  7.         this.dormitoryMember=dormitoryMember;  
  8.     }  
  9.     //代理买主食   
  10.     public boolean buyStapleFood(String stapleFoodName) {  
  11.         boolean buySuccess=dormitoryMember.buyStapleFood(stapleFoodName);  
  12.         if( buySuccess=false){  
  13.             System.out.println("给小孙打电话");  
  14.             return false;  
  15.         }else{  
  16.         return true;  
  17.         }  
  18.     }  
  19.     //代理买菜   
  20.     public boolean buyDish(String dishName) {  
  21.         boolean buySuccess=dormitoryMember.buyDish(dishName);  
  22.         if( buySuccess=false){  
  23.             System.out.println("给小孙打电话");  
  24.             return false;  
  25.         }else{  
  26.         return true;  
  27.         }  
  28.     }  
  29.     //代理买饮料   
  30.     public boolean buyDrink(String drinkName) {  
  31.         boolean buySuccess=dormitoryMember.buyDrink(drinkName);;  
  32.         if( buySuccess=false){  
  33.             System.out.println("给小孙打电话");  
  34.             return false;  
  35.         }else{  
  36.         return true;  
  37.         }  
  38.     }  
  39. }  
//代理类
public class DormitoryMemberProxy implements DormitoryMember {
	//持有一个被代理的对象
    private DormitoryMember dormitoryMember;
    //构造时传入被代理对象
    public DormitoryMemberProxy(DormitoryMember dormitoryMember){
    	this.dormitoryMember=dormitoryMember;
    }
    //代理买主食
	public boolean buyStapleFood(String stapleFoodName) {
		boolean buySuccess=dormitoryMember.buyStapleFood(stapleFoodName);
		if( buySuccess=false){
			System.out.println("给小孙打电话");
			return false;
		}else{
		return true;
		}
	}
    //代理买菜
	public boolean buyDish(String dishName) {
		boolean buySuccess=dormitoryMember.buyDish(dishName);
		if( buySuccess=false){
			System.out.println("给小孙打电话");
			return false;
		}else{
		return true;
		}
	}
    //代理买饮料
	public boolean buyDrink(String drinkName) {
		boolean buySuccess=dormitoryMember.buyDrink(drinkName);;
		if( buySuccess=false){
			System.out.println("给小孙打电话");
			return false;
		}else{
		return true;
		}
	}
}

    问题(1)

    看代码可以知道,我们要对代理类中的每一个方法都进行同样的修改。极限假设我们实现的这个接口有1000个方法,哭死都不冤枉。

    问题(2)

     极限假设,伟大的宿舍长不止帮小孙买饭,还要帮小李买衣服,还要帮小赵买电脑。换句话说宿舍长要代理不同的接口。但是这是不可能实现的,因为在代理类所持有的实际类是确定的,也就是说每一个代理类只能代理一个借口。
    显然很不科学,这么多的代码重复出现。有不科学的情况出现,自然就会有办法因对。最怕的是没有发现问题,没有问题就没有改进。下面我们上动态代理。

动态代理

    首先看问题一,既然当代理调用实际类时在代理类中的预处理都是一样的那么我们要达到代码不重复的话就只要保证调用实际类之前或者之后都回掉到某个处理就好了。当然以前好像没有学过可以对很多种不同的方法都同样回掉一个函数,因为类的参数个数和类型以及返回值都是不一致的。因此,java中就为此专门有一个接口可以实现这样的功能,InvocationHandler接口。

    再看问题二,对于问题二我们必须将代理类和被代理类解耦,通常我们解决这类问题就是用反射。这也不例外,这里java提供的反射机制可以在运行时产生代理类,即动态代理。这篇博客已经太长了,分开来吧。先把动态代理的代码贴上。其中实际类和接口没有变化!

     代理类工厂    

[java] view plaincopyprint?

  1. import java.lang.reflect.Proxy;  
  2. //代理工厂类   
  3. public class ProxyFactory {  
  4.     //实际类   
  5.      private Object target;  
  6.      public ProxyFactory(Object target){  
  7.          this.target=target;  
  8.      }  
  9.      //根据传入的实际类生成代理   
  10.      public Object getInstance(){  
  11.          //实例化一个继承了InvocationHandler接口的预处理对象
      
  12.          ProxyHandler handler=new ProxyHandler(target);  
  13.         //反射得到代理类   
  14.          return Proxy.newProxyInstance(target.getClass().getClassLoader(),  
  15.                  target.getClass().getInterfaces(),  
  16.                  handler);//handler即集中处理在动态代理类对象上的方法调用  
      
  17.      }  
  18. }  
import java.lang.reflect.Proxy;
//代理工厂类
public class ProxyFactory {
	//实际类
     private Object target;
     public ProxyFactory(Object target){
    	 this.target=target;
     }
     //根据传入的实际类生成代理
     public Object getInstance(){
    	 //实例化一个继承了InvocationHandler接口的预处理对象
    	 ProxyHandler handler=new ProxyHandler(target);
    	//反射得到代理类
    	 return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                 target.getClass().getInterfaces(),
                 handler);//handler即集中处理在动态代理类对象上的方法调用
     }
}

     继承了InvocationHandler接口的handler类

[java] view plaincopyprint?

  1. import java.lang.reflect.InvocationHandler;  
  2. import java.lang.reflect.Method;  
  3.   
  4.   
  5. public class ProxyHandler implements InvocationHandler {  
  6.     private Object target;  
  7.     public ProxyHandler(Object target){  
  8.         this.target=target;  
  9.         }  
  10.     //所有代理方法的公共处理方法   
  11.     public Object invoke(Object proxy, Method method, Object[] args)  
  12.             throws Throwable {  
  13.         Object result =null;  
  14.         result=method.invoke(target, args);  
  15.         //要买的东西断货时   
  16.         if(result.toString().equals("false")){  
  17.             System.out.println("给小孙打电话");  
  18.             return result;  
  19.         }else{  
  20.         return result;  
  21.         }  
  22.     }  
  23. }  
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ProxyHandler implements InvocationHandler {
	private Object target;
	public ProxyHandler(Object target){
		this.target=target;
		}
	//所有代理方法的公共处理方法
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result =null;
		result=method.invoke(target, args);
		//要买的东西断货时
		if(result.toString().equals("false")){
			System.out.println("给小孙打电话");
			return result;
		}else{
		return result;
		}
	}
}

       修改后的客户端调用

[java] view plaincopyprint?

  1. public class Client {  
  2.    public static void main(String[] args){  
  3.        //真正得到的代理类在客户端强转
      
  4.        DormitoryMember dormitoryMember=(DormitoryMember)new ProxyFactory(new DormitoryMemberImpl()).getInstance();  
  5.        dormitoryMember.buyStapleFood("米饭");  
  6.    }  
  7. }  
public class Client {
   public static void main(String[] args){
	   //真正得到的代理类在客户端强转
	   DormitoryMember dormitoryMember=(DormitoryMember)new ProxyFactory(new DormitoryMemberImpl()).getInstance();
	   dormitoryMember.buyStapleFood("米饭");
   }
}

     总结:动态代理通过反射把代理类和实际类之间的耦合解开了,同时通过继承了InvocationHandler接口的handler类对代理的方法进行统一的处理。也就解决静态代理所遇到的问题。

 

 

下面就动态代理的实现原理做一个介绍,从而有一个更深入的理解。当然,这也已经渐渐的脱离的代理模式的内容,因为学习是一个管中窥豹的过程,了解的越深入才能了解事务的本质。

    问题重现

    动态代理呢解决了静态代理的两个在一定条件下不适应的问题:其一,因为代理类中需要持有一个实际类而导致的代理类和实际类的耦合度过高的问题。其二,当接口的方法过多时,代理类的重复代码过多的问题。第一个问题是通过Proxy类的newProxyInstance方法动态的创建代理类解决的。第二个问题则是通过一个继承了InvocationHandler接口的类的invoke方法解决的。这里有三个关键词,分别是:动态、Proxy、InvocationHandler。下面我们一一解释!

    动态

    动态是指在运行状态下加载、探知、使用编译期间完全未知的classes。换句话说,Java程序可以加载一个运行时才得知名称的class,并生成其对象实体、或改变其属性、或唤起其methods。那么放到动态代理里就是说,代理类在程序运行之前是不存在的,而是在程序运行中才创建的。即在程序运行中才有实际的代理类产生并实例话。

    InvocationHandler接口

    静态代理的第二个问题是,每个代理的方法都要加上相同内容的代码,重复代码太多。实现了这个接口的类,是作为代理类的一个关联存在的。也就是说这个类是面对代理类中的所有方法的,它把代理类的功能分成了两个部分。对方法的加强放在了这个类里,而代理类则只实现对实际类的方法调用。
    这个接口中只有一个Invoke方法这个方法就是对代理类的方法的加强。它实际是对代理方法的一个拦截处理,也就是说当调用代理类中的方法时,不论哪一个都相当于调用了Invoke方法。而在Invoke方法中对代理的方法做了一个包装加强。Invoke方法有三个参数如下:

[java] view plaincopyprint?

  1. public Object invoke(Object proxy, Method method, Object[] args)  
  2.         throws Throwable {  
  3.         //代码略   
  4.     }  
public Object invoke(Object proxy, Method method, Object[] args)
		throws Throwable {
		//代码略
	}

         proxy :在其上调用方法的代理实例
         method:对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
         args: 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。

    Proxy类

    这个类是动态的产生代理类的关键,先看看下表对该类的方法有些了解。这其中目前我觉得比较重要的是NewProxyInstance方法。所以这个介绍一下,其他的就查Java的api文档吧。

[java] view plaincopyprint?

  1. public static Object newProxyInstance(ClassLoader loader,  
  2.                                       Class<?>[] interfaces,  
  3.                                       InvocationHandler h)<span style="font-family: FangSong_GB2312;">              </span>  
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)<span style="font-family: FangSong_GB2312;">              </span>

    参数:
         loader - 定义代理类的类加载器
         interfaces - 代理类要实现的接口列表
         h - 指派方法调用的调用处理程序
    返回:

         一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口              

        总结:到这里动态代理包含的内容都做了基本介绍,那么经过这几天的学习呢其实动态代理我们只要分开两个过程看,一个是代理的创建过程如下图,一个是代理模式的实现,这个就不上图了。这里需要强调的是代理模式的类图在动态代理中具有时间性,因为没有创建代理之前那么代理模式是不存在的。

     那么这里真正新奇的东西是Proxy类中的newProxyInstance方法,几个参数的说明都是莫名其妙的。这里涉及到java的另一知识:反射机制。而动态代理只是它的一个典型应用。那么下篇博客再见了,反射机制!

详见链接:

http://blog.csdn.net/zhuojiajin/article/details/38871145

时间: 2024-09-13 09:34:16

代理模式深入的相关文章

大话设计模式之代理模式

代理模式的应用场合: 一,远程代理 二,虚拟代理 三,安全代理 简单来说,就是两个类共同实现接口,一个实现,一个调用这个实现.而调用实现的类,就是代理类. 针对书上的代码,我加了一个NAME变量和一个SHOWNAME方法.更加直观展示代理的细节. 1 /* 2 * Created by SharpDevelop. 3 * User: home 4 * Date: 2013/4/21 5 * Time: 9:33 6 * 7 * To change this template use Tools

设计模式之代理模式(Proxy Pattern)

1 代理模式定义 定义:给某个对象提供一个代理对象,并由代理对象控制对于原对象的访问,即客户不直接操控原对象,而是通过代理对象间接地操控原对象. 本篇文章主要介绍的是静态代理,关于动态代理请参考:设计模式之动态代理(dynamic proxy) 2 代理模式的作用 在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 通过代理类对原有类进行功能增强,如增加缓存.异常处理.日志处理等,Spring AOP用的就是(动态)代理模式 3 代理模式一

javascript 之 代理模式

代理模式说明 说明:顾名思义就是用一个类来代替另一个类来执行方法功能,这个模式跟装饰模式有点相似,不一样的是,代理模式是代替客户初始化被代理对象类,而装饰模式采用接口或初装饰者参数引用的方式来执行的. 在动态面向对象语言里,代理模式即起到控制修饰被代理类的作用,也对被代理类起到了充分的隐藏保护作用:被代理类只到我们需要时,才被间接初始化调用: 场景描述: 很常见到的就是租房子的例子,房客要租房子,房东要把房子租出去,但是房客跟房东都没怎么有空找房子或在家等着有人看房子,就一同去找房屋中介.房东把

php设计模式 Proxy (代理模式)

复制代码 代码如下: <?php /** * 代理模式 * * 为其他对象提供一个代理以控制这个对象的访问 * */ interface Proxy { public function request(); public function display(); } class RealSubject { public function request() { echo "RealSubject request<br/>"; } public function disp

代理模式(proxy pattern) 未使用代理模式 详解

如果需要监控(monitor)类的某些状态, 则需要编写一个监控类, 并同过监控类进行监控. 但仅仅局限于本地, 如果需要远程监控, 则需要使用代理模式(proxy pattern). 具体方法: 1. 类中需要提供状态信息, 并提供一些get方法, 进行调用. /** * @time 2014年7月11日 */ package proxy; /** * @author C.L.Wang * */ public class GumballMachine { String location; //

传统设计模式(十一)代理模式

所谓的代理,就是代表某个真实的对象.在这个设计模式中,代理可以假装自己是远程对象,但其实只是一个中间角色.客户对象所作的就像是在做远程方法调用,但其实只是调用本地资源中得"代理"对象上得方法,再由代理处理所有网络通信的底层细节. 其实其实项目实例神马的 根本就没必要了 看一下Web Service的调用方式大家也许就明白了,它会在客户端生成一个代理类 - - 已经很完美的诠释了代理模式这个概念 虫子放下水 直接拿以前监控项目中客户端采集的代理方法了 --_____-- 服务器端 [We

Java的代理模式

前几天一个网友指出了我的文章中一些有失偏颇之处,那些文章都是我在阅读Java Core的时候做的总结,顺便加上我个人的一些理解.因为看的e文版,理解上有些地方可能还欠妥.下面谈一下对Java中代理模式(Proxy)的认识. 代理,想必大家都应该知道是什么冬冬了,一般的手机产商都有代理商,歌星们都有自己的经纪人,如此这些都可以看作是一种代理模式.下面我选择如下的一种情景来进行讲述:某董事长出差,但是此时公司有个聚会,董事长买单,但是由他的秘书去结帐.我们就权且把这个看作一个代理行为,^_^. 首先

浅析.NET开发中代理模式的使用

当我们需要使用的对象很复杂或者需要很长时间去构造,这时就可以使用代理模式(Proxy).例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象.一个代理(Proxy)通常包含和将要使用的对象同样的方法,一旦开始使用这个对象,这些方法将通过代理(Proxy)传递给实际的对象. 一些可以使用代理模式(Proxy)的情况: 1.一个对象,比如一幅很大的图像,需要载入的时间很长. 2.一个需要很长时间才可以完成的计算结果,并且需要在它计算过程

解读设计模式----代理模式(Proxy Pattern)

一.说买电脑的那些事 我之前一直用的是台式机,可总感觉不方便,特别是携带,就拿租房子后搬家来说吧,费了不少劲.种种原因有了想换笔记本的想法.5.1假期和一个好朋友特到电脑城去逛了一圈,一进电脑城便见:"HP笔记本XX总代理.IBM笔记本专卖.XX电脑YY总代理......". 看了很多家销售店,给我印象最深的就是到处都是XX牌电脑专卖和代理商,就在同一层楼里我就见着同一牌子有6家代理销售商铺.呵呵,看来我们买电脑就直接找销售代理商就可以,没必要在找生产厂商了,厂商生产出电脑后就是要把产

代理模式-动态代理

package pattern.proxy.dynamic; import java.lang.reflect.Proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * 代理模式:Proxy Pattern * Java对代理模式的支持: * java.lang.reflect.Proxy.java.lang.reflect.InvocationHandler. * @ver