http://blog.csdn.net/zhuojiajin/article/details/38531539
故事
周末放假,小孙睡到12点才告别周公醒来,顿时饥肠辘辘。舍长小王正准备去食堂买饭,作为一个好舍长小王主动要帮小孙带饭。小孙点了米饭、宫保鸡丁、芬达。小孙起床洗漱,然后静待舍长。小孙心理寻思道舍长果然是好舍长啊。下面我们先把这个故事抽象一下,画作类图。这个类图即代理模式。
代理模式
定义:为其他对象提供一种代理以控制对这个对象的访问。怎么理解这句话呢?从生活的角度来说就是:用一个对象A代替另一个对象B来处理本来应该由对象B完成的事情,例如上面买饭的例子,又如通过黄牛买票等等。从代码的角度来说则是:用一个对象A来过滤、预处理对对象B的调用 。不管从哪个角度看吧,代理都需要拥有和实际对象的需要被代理的全部方法。
静态代理
类图如上所示了,定义也看了。接下来我们按照类图来看看代码。
接口(宿舍成员)
[java] view plaincopyprint?
- /**
- * 宿舍成员接口
- * 开发时间:2014-8-12 下午9:17:19
- */
- public interface DormitoryMember {
- //买主食
- public boolean buyStapleFood(String stapleFoodName);
- //买菜
- public boolean buyDish(String dishName);
- //买饮料
- public boolean buyDrink(String drinkName);
- }
/** * 宿舍成员接口 * 开发时间: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?
- //代理类
- 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);
- }
- }
//代理类 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?
- //实际的类
- 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;
- }
- }
//实际的类 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?
- //代理类
- 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;
- }
- }
- }
//代理类 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?
- 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即集中处理在动态代理类对象上的方法调用
- }
- }
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?
- 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;
- }
- }
- }
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?
- public class Client {
- public static void main(String[] args){
- //真正得到的代理类在客户端强转
- DormitoryMember dormitoryMember=(DormitoryMember)new ProxyFactory(new DormitoryMemberImpl()).getInstance();
- dormitoryMember.buyStapleFood("米饭");
- }
- }
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?
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- //代码略
- }
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //代码略 }
proxy :在其上调用方法的代理实例
method:对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
args: 包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。
Proxy类
这个类是动态的产生代理类的关键,先看看下表对该类的方法有些了解。这其中目前我觉得比较重要的是NewProxyInstance方法。所以这个介绍一下,其他的就查Java的api文档吧。
[java] view plaincopyprint?
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- 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