圖-代理模式java
代理設計模式是在程序開發之中使用最多的設計模式,代理設計模式的核心是有真實業務實現類與代理業務實現類,而且代理類要完成比真實業務更多的處理操做。編程
全部的代理設計模式若是按照設計要求來說,必須是基於接口的設計,也就是說須要首先定義出核心接口的組成。下面模擬一個消息發送的代理操做結構。設計模式
·範例:傳統代理設計ide
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個代理類的操做形式相似。函數
因此如今須要解決的問題:【如何讓一個代理類知足於全部的業務接口操做要求?】this
經過靜態代理設計模式的缺陷能夠發現,最好的作法是爲全部功能一致的業務接口提供有統一的代理處理操做,而這就能夠經過動態代理機制來實現,可是在動態代理機制裏面須要考慮到以下幾點問題:spa
①無論是動態代理類仍是靜態代理類都必定要接收真實業務實現子類對象;設計
②因爲動態代理類再也不與某一個接口進行捆綁,因此應該能夠動態獲取類的接口信息;代理
在進行動態代理實現的過程操做之中,首先須要關注的就是一個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()方法你會發現該方法會大量使用底層機制來進行代理對象的動態建立,全部的代理類是符合全部相關功能須要求的操做功能類,它不在表明具體的接口,這樣在處理的時候就必須依賴於類加載器與接口進行代理對象的僞造。
從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 */
在進行代理設計模式定義的時候除了可使用接口以外,也能夠不受接口的限制而實現基於類的代理設計,可是若是從正常的設計角度來說,強烈建議仍是基於接口的設計會比較合理。