代理模式(思维导图)
图-代理模式
1,静态代理设计模式
代理设计模式是在程序开发之中使用最多的设计模式,代理设计模式的核心是有真实业务实现类与代理业务实现类,并且代理类要完成比真实业务更多的处理操作。
传统代理模式设计的弊端
所有的代理设计模式如果按照设计要求来讲,必须是基于接口的设计,也就是说需要首先定义出核心接口的组成。下面模拟一个消息发送的代理操作结构。
·范例:传统代理设计
1 package cn.mufasa.demo1; 2 3 public interface IMessage {//传统代理模式必须有接口 4 public void send();//业务方法 5 } 6 public class MessageReal implements IMessage{ 7 @Override 8 public void send() { 9 System.out.println("【消息发送】www.cnblogs.com"); 10 } 11 } 12 public class MessageProxy implements IMessage{//代理类 13 private IMessage message;//代理对象,一定是业务接口实例 14 public MessageProxy(IMessage message){ 15 this.message=message; 16 } 17 public boolean connect(){ 18 System.out.println("【消息代理】进行消息发送通道的链接"); 19 return true; 20 } 21 public void close(){ 22 System.out.println("【消息代理】关闭消息通道"); 23 } 24 @Override 25 public void send() { 26 if(this.connect()){ 27 this.message.send();//消息发送处理 28 this.close(); 29 } 30 } 31 } 32 public class JavaAPIDemo { 33 public static void main(String[] args) { 34 IMessage msg=new MessageProxy(new MessageReal());//最常用的做法 35 msg.send(); 36 } 37 } 38 /* 39 【消息代理】进行消息发送通道的链接 40 【消息发送】www.cnblogs.com 41 【消息代理】关闭消息通道 42 */
以上的操作代码是一个最为标准的代理设计,但是如果要进一步的去思考会发现客户端的接口与具体的子类产生耦合问题,所以这样的操作如果从实际的开发来讲最好在引入工厂设计模式进行代理对象的获取。
以上为【静态代理模型】,这种静态代理模式的特点在于:一个代理类只为一个接口服务,那么说现在准备出了3000个业务接口,则按照此种做法就意味着需要编写3000个代理类,并且这3000个代理类的操作形式类似。
所以现在需要解决的问题:【如何让一个代理类满足于所有的业务接口操作要求?】
2,动态代理设计模式
通过静态代理设计模式的缺陷可以发现,最好的做法是为所有功能一致的业务接口提供有统一的代理处理操作,而这就可以通过动态代理机制来实现,但是在动态代理机制里面需要考虑到如下几点问题:
①不管是动态代理类还是静态代理类都一定要接收真实业务实现子类对象;
②由于动态代理类不再与某一个接口进行捆绑,所以应该可以动态获取类的接口信息;
在进行动态代理实现的过程操作之中,首先需要关注的就是一个InvocationHandler接口,这个接口规定了代理方法的执行,
1 public interface InvocationHandler1 { 2 /** 3 * 代理方法调用,代理主题类里面执行的方法最终都是次方法 4 * @param proxy 要代理的对象 5 * @param method 要执行的接口方法名称 6 * @param args 传递的参数 7 * @return 某一个方法的返回值 8 * @throws Throwable 方法调用时出现错误继续向上抛出 9 */ 10 public Object invoke(Object proxy, Method method, Object[] args) 11 throws Throwable; 12 }
在进行动态代理设计的时候对于动态对象的创建是由JVM底层完成的,此时主要依靠的是java.lang.reflect.Proxy程序类,而这个程序类之中只提供一个核心方法:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
|-ClassLoader loader:获取当前真实主体类的ClassLoader
|-Class<?>[] interfaces:代理是围绕接口进行的,所以一定要获取真实主题类的接口信息;
|-InvocationHandler h:代理处理的方法。
·范例:实现动态代理机制
1 package cn.mufasa.demo2; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 public class MUFASAProxy implements InvocationHandler { 8 private Object target;//保存真实业务主题对象 9 10 /** 11 * 进行真实业务对象与代理业务对象之间的绑定处理 12 * @param target 真实业务对象 13 * @return Proxy生成的代理业务对象 14 */ 15 public Object bind(Object target){ 16 this.target=target; 17 return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this); 18 } 19 public boolean connect(){ 20 System.out.println("【消息代理】进行消息发送通道的链接"); 21 return true; 22 } 23 public void close(){ 24 System.out.println("【消息代理】关闭消息通道"); 25 } 26 @Override 27 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 28 System.out.println("【执行方法】"+method); 29 Object returnData=null; 30 if(this.connect()){ 31 returnData=method.invoke(this.target,args); 32 this.close(); 33 } 34 return returnData; 35 } 36 }
1 package cn.mufasa.demo2; 2 3 public class JavaAPIDemo { 4 public static void main(String[] args) { 5 IMessage msg=(IMessage) new MUFASAProxy().bind(new MessageReal());//最常用的做法 6 msg.send(); 7 } 8 } 9 /* 10 【执行方法】public abstract void cn.mufasa.demo2.IMessage.send() 11 【消息代理】进行消息发送通道的链接 12 【消息发送】www.cnblogs.com 13 【消息代理】关闭消息通道 14 */
如果你认真观察系统中提供的Proxy.newInstance()方法你会发现该方法会大量使用底层机制来进行代理对象的动态创建,所有的代理类是符合所有相关功能需要求的操作功能类,它不在代表具体的接口,这样在处理的时候就必须依赖于类加载器与接口进行代理对象的伪造。
3,CGLIB实现代理设计模式
从Java的官方来讲已经明确的要求了如果要想实现代理设计模式,那么一定是基于接口的应用,所以在官方给出的Proxy类创建代理对象时,都需要传递该对象的所有接口信息: Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
但是这个时候有一部分的开发者就认为不应该强迫性的基于接口来实现代理设计【使用函数式编程或者其他】,所以一些开发者就开发了一个CGLIB的开发包,利用这个开发就可以实现基于类的代理设计模式。
①CGLIB是一个第三方的程序包,需要单独在【Eclipse】之中进行配置!我使用的IDEA,需要打开Eclipse项目属性安装第三方开发包;
②编写程序类,该类不实现任何接口;
1 public class MessageReal{ 2 public void send() { 3 System.out.println("【消息发送】www.cnblogs.com"); 4 } 5 }
按照官方的定义此时无法变成代理的,但是CGLIB可以使用类来实现接口
③利用CGLIB编写代理类,但是这个代理类做一个明确,此时相当于使用了类的形式实现了代理设计的处理,所以该代理设计需要通过CGLIB来生成代理对象,定义一个代理类;
1 package cn.mufasa.demo3; 2 3 import net.sf.cglib.proxy.MethodInterceptor; 4 import net.sf.cglib.proxy.MethodProxy; 5 import java.lang.reflect.Method; 6 7 public class MUFASAProxy implements MethodInterceptor { 8 private Object target;//保存真实业务主题对象 9 public MUFASAProxy(Object target){ 10 this.target=target; //保存真实主题对象 11 } 12 @Override 13 public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 14 Object returnData=null; 15 if(this.connect()){ 16 returnData=method.invoke(this.target,args); 17 this.close(); 18 } 19 return returnData; 20 } 21 22 public boolean connect(){ 23 System.out.println("【消息代理】进行消息发送通道的链接"); 24 return true; 25 } 26 public void close(){ 27 System.out.println("【消息代理】关闭消息通道"); 28 } 29 }
④此时如果要想创建代理类对象,则就必须进行一系列的CGLIB处理。
1 package cn.mufasa.demo3; 2 import net.sf.cglib.proxy.Enhancer; 3 public class JavaAPIDemo { 4 public static void main(String[] args) { 5 Message realObject=new Message();//真实主体对象 6 Enhancer enhancer=new Enhancer();//负责代理操作的程序类 7 enhancer.setSuperclass(realObject.getClass());//假定一个父类 8 enhancer.setCallback(new MUFASAProxy(realObject));//设置代理类 9 Message proxyObject=(Message) enhancer.create();//创建代理对象,向下转型 10 proxyObject.send(); 11 } 12 } 13 /* 14 【执行方法】public abstract void cn.mufasa.demo2.IMessage.send() 15 【消息代理】进行消息发送通道的链接 16 【消息发送】www.cnblogs.com 17 【消息代理】关闭消息通道 18 */
在进行代理设计模式定义的时候除了可以使用接口之外,也可以不受接口的限制而实现基于类的代理设计,但是如果从正常的设计角度来讲,强烈建议还是基于接口的设计会比较合理。