一、代理模式是什么?
代理模式属于GOF23设计模式中结构型中的设计模式,通过代理对象来屏蔽(部分或者屏蔽)对真实对象的直接访问,下图为UML图:
在代理模式中组件包括:抽象角色接口、代理角色类、真实角色类。
抽象角色:声明真实对象和代理对象的共同接口。
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。
实际上代理又分为动态代理和静态代理,在实际开发中使用比较多的是动态代理。在主题三种将分别对动态代理和静态代理给出具体的示例。
所谓的静态代理就是由程序员创建代理类或特定工具自动生成源代码再对其编译。在程序运行前代理类的.class文件就已经存在了。而动态代理就是在程序运行时运用反射机制动态创建而成。
二、为什么用代理模式
1、远程代理,为一个对象在不同的地址空间提供局部代表。这样可以隐藏一个对象存在于不同地址空间的事实。
2、虚拟代理,根据需要创建开销很大的对象。通过它来存放实例化需要很长时间的真实对象。例如,网页中在图片出来以前现出来文字。
3、安全代理,用来控制真实对象访问时的权限。
4、智能代理,是指当调用真实的对象时,代理处理另外一些事。
注:代理模式是AOP编程的核心实现之一。
三、怎么用代理模式
3.1:静态代理示例
源码已经上传GitHub:https://github.com/wanyouxian/DesignPatterns,工程名为:StaticProxy
定义抽象角色接口:
1 package com.rocky.staticproxy; 2 3 public interface IRenting 4 { 5 /* 6 * 看房子 7 */ 8 public void watchingHouse(); 9 10 /** 11 * 签合同 12 */ 13 public void signContract(); 14 15 /** 16 * 取钥匙 17 */ 18 public void takeKey(); 19 }
定义代理对象类,实现抽象角色接口,该类持有真实对象引用
1 package com.rocky.staticproxy; 2 3 public class HouseProxy implements IRenting 4 { 5 6 private IRenting landLord = null; 7 8 public HouseProxy(IRenting landLord) 9 { 10 this.landLord = landLord; 11 } 12 13 @Override 14 public void watchingHouse() 15 { 16 System.out.println("HouseProxy.watchingHouse"); 17 } 18 19 @Override 20 public void signContract() 21 { 22 landLord.signContract(); 23 } 24 25 @Override 26 public void takeKey() 27 { 28 System.out.println("HouseProxy.takeKey"); 29 } 30 }
定义真实对象类,实现抽象接口
1 package com.rocky.staticproxy; 2 3 public class LandLord implements IRenting 4 { 5 6 @Override 7 public void watchingHouse() 8 { 9 System.out.println("LandLord.watchingHouse"); 10 } 11 12 @Override 13 public void signContract() 14 { 15 System.out.println("LandLord.signContract"); 16 } 17 18 @Override 19 public void takeKey() 20 { 21 System.out.println("LandLord.takeKey"); 22 } 23 }
编写测试类
1 package com.rocky.testdriver; 2 3 import com.rocky.staticproxy.HouseProxy; 4 import com.rocky.staticproxy.IRenting; 5 import com.rocky.staticproxy.LandLord; 6 7 public class TestDriver 8 { 9 10 public static void main(String[] args) 11 { 12 IRenting landLord = new LandLord(); 13 IRenting houseProxy = new HouseProxy(landLord); 14 15 houseProxy.watchingHouse(); 16 17 houseProxy.signContract(); 18 19 houseProxy.takeKey(); 20 } 21 22 }
3.2:动态代理示例
动态代理有多种实现方式,并且在开发中实际使用的比较多,特别是AOP编程的核心实现机制。AOP动态代理主要是通过jdk动态代理和cglib动态代理实现,jdk动态代理是由Java内部的反射机制来实现的,cglib动态代理底层则是借助asm来实现的。总的来说,反射机制在生成类的过程中比较高效,而asm在生成类之后的相关执行过程中比较高效(可以通过将asm生成的类进行缓存,这样解决asm生成类过程低效问题)。还有一点必须注意:jdk动态代理的应用前提,必须是目标类基于统一的接口。如果没有上述前提,jdk动态代理不能应用。下面示例中通过JDK实现动态代理作为演示demo,源码已经上传GitHub:https://github.com/wanyouxian/DesignPatterns,工程名为:DynamicProxy
定义抽象角色:
1 package com.rocky.dynamicproxy; 2 3 public interface IRenting 4 { 5 /* 6 * 看房子 7 */ 8 public void watchingHouse(); 9 10 /** 11 * 签合同 12 */ 13 public void signContract(); 14 15 /** 16 * 取钥匙 17 */ 18 public void takeKey(); 19 }
定义真实对象类:
1 package com.rocky.dynamicproxy; 2 3 public class LandLord implements IRenting 4 { 5 6 @Override 7 public void watchingHouse() 8 { 9 System.out.println("房东把房子挂到中介"); 10 } 11 12 @Override 13 public void signContract() 14 { 15 System.out.println("房东自己签租房合同"); 16 17 } 18 19 @Override 20 public void takeKey() 21 { 22 System.out.println("中介把房子钥匙给房客"); 23 24 } 25 26 }
定义代理对象处理器:
1 package com.rocky.dynamicproxy; 2 3 public class LandLord implements IRenting 4 { 5 6 @Override 7 public void watchingHouse() 8 { 9 System.out.println("房东把房子挂到中介"); 10 } 11 12 @Override 13 public void signContract() 14 { 15 System.out.println("房东自己签租房合同"); 16 17 } 18 19 @Override 20 public void takeKey() 21 { 22 System.out.println("中介把房子钥匙给房客"); 23 24 } 25 26 }
编写测试类:
1 package com.rocky.testdriver; 2 3 import java.lang.reflect.Proxy; 4 5 import com.rocky.dynamicproxy.IRenting; 6 import com.rocky.dynamicproxy.LandLord; 7 import com.rocky.dynamicproxy.RentInvocationHandler; 8 9 public class TestDriver 10 { 11 12 public static void main(String[] args) 13 { 14 IRenting landLord = new LandLord(); 15 RentInvocationHandler handler = new RentInvocationHandler(landLord); 16 17 IRenting proxy = (IRenting) Proxy.newProxyInstance(landLord.getClass().getClassLoader(), 18 landLord.getClass().getInterfaces(), handler); 19 20 proxy.watchingHouse(); 21 proxy.signContract(); 22 proxy.takeKey(); 23 } 24 25 }