以此之長,補彼之短----AOP(代理模式)

上文中提到代理分爲靜態代理和動態代理,採用代理是爲了經過不修改源代碼的狀況下給程序動態統一添加功能,利用代理技術能夠將業務邏輯中一些非業務邏輯的代碼分離出來,把他們獨立到業務邏輯類外,好比日誌記錄,性能統計,安全控制,事務處理,異常處理等。這樣作,不只下降了業務邏輯和非業務邏輯的耦合性,提升程序的可重用性,同時提升了開發的效率。 java

 

下面以添加日誌記錄爲例,分析靜態代理的使用。建立一個用戶管理類UserManagerImpl,並建立添加用戶方法addUser,爲其良好擴展性,建立一個通用接口UserManager,代碼分別以下: 程序員

接口代碼數組

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public interface UserManager {  
  4.   
  5.     public void addUser(String userId,String userName);  
  6. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public interface UserManager {  
  4.   
  5.     public void addUser(String userId,String userName);  
  6. }  

實現類代碼安全

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class UserManagerImpl implements UserManager {  
  4.   
  5.     public void addUser(String userId, String userName) {  
  6.           
  7.         try {  
  8.             //System.out.println("開始執行");   
  9.             System.out.println("HelloWorld!");  
  10.             //System.out.println("執行成功!");   
  11.         }catch(Exception e) {  
  12.             e.printStackTrace();  
  13.             //System.out.println("執行失敗!");   
  14.             throw new RuntimeException();  
  15.         }     
  16.     }  
  17. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class UserManagerImpl implements UserManager {  
  4.   
  5.     public void addUser(String userId, String userName) {  
  6.           
  7.         try {  
  8.             //System.out.println("開始執行");  
  9.             System.out.println("HelloWorld!");  
  10.             //System.out.println("執行成功!");  
  11.         }catch(Exception e) {  
  12.             e.printStackTrace();  
  13.             //System.out.println("執行失敗!");  
  14.             throw new RuntimeException();  
  15.         }     
  16.     }  
  17. }  

從代碼能夠看出,註釋裏面的日誌內容和業務邏輯毫無關係,無形中使耦合性增長,若是不少類中須要添加這些日誌代碼,工做量不言而喻,修改起來也很是麻煩。若是採用靜態代理把打印日誌的代碼抽取到代理類中,經過代理類和業務邏輯類繼承自同一個父類,客戶端直接調用代理類完成需求,這樣就解決了客戶端與業務邏輯類的耦合。示例代碼以下: app

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3.   
  4. public class UserManagerImplProxy implements UserManager{  
  5.   
  6.   
  7.     private UserManager userManager;  
  8.       
  9.     public UserManagerImplProxy(UserManager userManager){  
  10.         this.userManager = userManager;  
  11.     }  
  12.       
  13.     @Override  
  14.     public void addUser(String userId, String userName) {  
  15.         try {  
  16.             System.out.println("開始執行");  
  17.             userManager.addUser(userId, userName);  
  18.             System.out.println("執行成功!");  
  19.         }catch(Exception e) {  
  20.             e.printStackTrace();  
  21.             System.out.println("執行失敗!");  
  22.         }             
  23.     }  
  24. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3.   
  4. public class UserManagerImplProxy implements UserManager{  
  5.   
  6.   
  7.     private UserManager userManager;  
  8.       
  9.     public UserManagerImplProxy(UserManager userManager){  
  10.         this.userManager = userManager;  
  11.     }  
  12.       
  13.     @Override  
  14.     public void addUser(String userId, String userName) {  
  15.         try {  
  16.             System.out.println("開始執行");  
  17.             userManager.addUser(userId, userName);  
  18.             System.out.println("執行成功!");  
  19.         }catch(Exception e) {  
  20.             e.printStackTrace();  
  21.             System.out.println("執行失敗!");  
  22.         }             
  23.     }  
  24. }  

客戶端調用代碼以下: ide

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());  
  10.         userManager.addUser("0111""張三");  
  11.     }  
  12.   
  13. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());  
  10.         userManager.addUser("0111""張三");  
  11.     }  
  12.   
  13. }  

靜態代理雖隔離了與業務邏輯無關的代碼,下降了耦合,讓業務邏輯類更專一於業務邏輯,但沒法減小代碼量,系統重複代碼過多,加大了程序員工做量。所以,JDK動態代理完美解決了此問題,動態代理支持在系統運行期給類動態添加代理,而後經過操控代理類完成對目標類的調用。 性能

 

繼續演化上面舉的例子,將靜態代理改成動態代理,抽象類UserManager和目標類UserManagerImpl中的代碼不變,將靜態代理類UserManagerImplProxy 刪除,添加LoadHandler類,並讓它實現InvocationHandler接口中的invoke方法,代碼以下: ui

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3.   
  4. import java.lang.reflect.InvocationHandler;  
  5. import java.lang.reflect.Method;  
  6. import java.lang.reflect.Proxy;  
  7.   
  8.   
  9. public class LogHandler implements InvocationHandler {  
  10.       
  11.     //保留一份targetObject目標類對象   
  12.     private Object targetObject;  
  13.       
  14.     //Proxy類動態建立一份目標代理類   
  15.     public Object newProxyInstance(Object targetObject){  
  16.         this.targetObject = targetObject;  
  17.         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);   
  18.     }  
  19.       
  20.     @Override  
  21.     public Object invoke(Object proxy, Method method, Object[] args)  
  22.             throws Throwable {  
  23.         System.out.println("開始執行!");  
  24.           
  25.         for(int i=0;i<args.length;i++){  
  26.             System.out.println(args[i]);  
  27.         }  
  28.         Object ret = null;  
  29.           
  30.         try{  
  31.             //調用目標方法   
  32.             ret = method.invoke(targetObject, args);  
  33.             System.out.println("執行成功!");  
  34.         }catch(Exception e){  
  35.             e.printStackTrace();  
  36.             System.out.println("執行失敗!");  
  37.             throw e;  
  38.         }  
  39.         return ret;  
  40.     }  
  41. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3.   
  4. import java.lang.reflect.InvocationHandler;  
  5. import java.lang.reflect.Method;  
  6. import java.lang.reflect.Proxy;  
  7.   
  8.   
  9. public class LogHandler implements InvocationHandler {  
  10.       
  11.     //保留一份targetObject目標類對象  
  12.     private Object targetObject;  
  13.       
  14.     //Proxy類動態建立一份目標代理類  
  15.     public Object newProxyInstance(Object targetObject){  
  16.         this.targetObject = targetObject;  
  17.         return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);   
  18.     }  
  19.       
  20.     @Override  
  21.     public Object invoke(Object proxy, Method method, Object[] args)  
  22.             throws Throwable {  
  23.         System.out.println("開始執行!");  
  24.           
  25.         for(int i=0;i<args.length;i++){  
  26.             System.out.println(args[i]);  
  27.         }  
  28.         Object ret = null;  
  29.           
  30.         try{  
  31.             //調用目標方法  
  32.             ret = method.invoke(targetObject, args);  
  33.             System.out.println("執行成功!");  
  34.         }catch(Exception e){  
  35.             e.printStackTrace();  
  36.             System.out.println("執行失敗!");  
  37.             throw e;  
  38.         }  
  39.         return ret;  
  40.     }  
  41. }  

Proxy類所建立的目標類必須實現至少一個接口,在調用newProxyInstance方法時必須與目標類的類加載器和接口一致;invoke方法很是相似Filter中的doFilter方法,它將調用目標類的全部方法在未到達UserManagerImpl以前截獲,根據咱們本身的需求進行預處理後,繼續調用UserManagerImpl。 this


爲了保持invoke方法的通用性,目標方法中的參數以數組args形式傳遞,若是方法中有返回值,則返回,沒有返回值,則返回null。如此一來,程序員沒必要爲每一個目標類設計一個代理類,全部須要打印日誌的類均可以共用這個LogHandler,若是不想使用日誌功能就能夠直接刪除LogHandler類,對原功能沒有絲毫影響,如同揭去顯示器上的保護膜,不會影響顯示器的使用通常。 spa

客戶端調用代碼以下:

[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.   
  10.         LogHandler logHandler = new LogHandler();  
  11.           
  12.         UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());  
  13.           
  14.         userManager.addUser("id""name");  
  15.     }  
  16.   
  17. }  
[java]   view plain copy print ?
  1. package com.snail.pattern;  
  2.   
  3. public class Client {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.   
  10.         LogHandler logHandler = new LogHandler();  
  11.           
  12.         UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());  
  13.           
  14.         userManager.addUser("id""name");  
  15.     }  
  16.   
  17. }  

經過以上兩篇博文我相信你們對AOP的原理和應用場合會有隻知其一;不知其二,文章中不免會出現錯誤,還望你們不吝賜教。

 

http://blog.csdn.net/stubbornpotatoes/article/details/7350256

相關文章
相關標籤/搜索