容器學習(二):動手模擬AOP

簡單來說,Spring是一個輕量級的控制反轉(IOC)和麪向切面(AOP)的容器框架。上文已經介紹模擬IoC實現,這篇文章來動手模擬AOP

 

AOP簡述

面向對象強調"一切皆是對象",是對真實世界的模擬然而面向對象也並非完美無缺的,它更注重於對象層次結構方面的東西,對於如何更好的管理對象行爲內部結構,還存在着些許不足。那麼我們如何使這個問題的得到更完美的解決呢?答案就是AOP

      AOPAspect-Oriented ProgrammingAOPOOP的補充,是GOF的延續。我們知道設計模式是對於面向對象設計中經驗的總結,它孜孜不斷追求的就是調用者與被調用者之間的解耦。有了設計模式我們可以更有效的利用面向對象的特性,使得整個軟件設計更加靈活、優雅。但是設計模式是基於面向對象的思想而形成的,更多的時候關注的是對象層次的東西,在解決對象行爲內部問題方面卻有些不足。AOP的出現恰恰就是對面向對象思想做出了完美的補充。

     說到AOP,我們就不得不來提一下軟件的縱向和橫向問題。從縱向結構來看就是我們軟件系統的各個模塊,它主要負責處理我們的核心業務(例如商品訂購、購物車查看);而從橫向結構來看,我們幾乎每個系統又包含一些公共模塊(例如權限、日誌模塊等)。這些公共模塊分佈於我們各個核心業務之中(例如訂購和查看商品明細的過程都需要檢查用戶權限、記錄系統日誌等)。這樣一來不僅在開發過程中要處處關注公共模塊的處理而且開發後維護起來也是十分麻煩。而有了AOP之後將應用程序中的商業邏輯同對其提供支持的通用服務進行分離,使得開發人員可以更多的關注核心業務開發。 



 

利用動態代理實現AOP

下面我們就以一個簡單的例子來看一下AOP吧!

點擊下載源碼:下載

比如說,我們現在要開發的一個應用裏面有很多的業務方法,但是,我們現在要對這個方法的執行做全面監控,或部分監控.也許我們就會在要一些方法執行前進行日誌記錄,我們寫個例子看看我們最簡單的解決方案

我們先寫一個接口UserManager.java代碼如下:

publicinterface UserManager {
publicvoid add(String userId, String userName);
}

 

add方法裏面只是將簡單的字符串作爲參數,而不是實體,勿怪,講解的重點不是這裏,我們去寫個類實現UserManager接口

publicclass UserManagerImp implements UserManager {
 
@Override
publicvoid add(String userId, String userName) {
System.out.println("成功執行:UserManagerImpl.add()userId-->>" + userId);
}
}

 

現在我們要爲這個業務方法加上日誌記錄的業務,我們在不改變原代碼的情況下,我們會去怎麼做呢?也許,你會去寫一個類去實現UserManager接口,並依賴UserManagerImp這個類.代碼如下:

public class UserManagerProxyimplements UserManager{  
 private UserManager userManager;  
   
 public UserManagerProxy(UserManager userManager){  
this.userManager= userManager;  
  }  
publicvoid addUser(String userId, String userName) {  
System.out.println("start-->>addUser()userId-->>" + userId);  
try{  
userManager.add(userId,userName);  
   
System.out.println("success-->>addUser()");  
}catch(Exceptione) {  
e.printStackTrace();  
System.out.println("error-->>addUser()");  
thrownew RuntimeException();  
}          
}  
}  


      從上面的代碼我們可以看出,UserManager 對象是被UserManagerProxy這個所謂的代理所創建的.這樣,如果我們以後要把日誌記錄的功能去掉.那我們只要把得到userManager對象的的具體實現改爲UserManager的就可以。上面的代碼 就是對AOP的最簡單的實現,但是我們接下來想,如果我們要在很多業務邏輯之前加日誌的話,那麼,我們是不是要去寫很多個UserManagerProxy這樣的類呢.其實也是一種很麻煩的事.jdk1.3以後.jdk跟我們提供了一個API java.lang.reflect.InvocationHandler的類這個類可以讓我們在JVM調用某個類的方法時動態的爲些方法做些什麼事.讓我們把以上的代碼改一下來看看效果.

 

我們一樣的去寫一個代理類.只不過.讓這個類去實現java.lang.reflect.InvocationHandler接口,代碼如下:

packagecom.java.drp.pattern;
 
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
 
publicclass LogHandler implements InvocationHandler {
/**
 * 要處理的對象(也就是我們要在方法前後加上業務邏輯的對象)
 */
privateObject targetObject;
 
/**
 * 動態生成方法被處理過後的對象 (寫法固定)
 * @param targetObject
 * @return
 */
publicObject newProxyInstance(Object targetObject){
this.targetObject= targetObject;
returnProxy.newProxyInstance(targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces(), this);
}
 
/**
 *要處理的對象中的每個方法會被此方法送去JVM調用,也就是說,要處理的對象的方法只能通過此方法調用
 * 此方法是動態的,不是手動調用的
 */
@Override
publicObject invoke(Object proxy, Method method, Object[] args)
throwsThrowable {
System.out.println("將要執行的方法:"+ method.getName());
 
System.out.print("將要執行的方法中的參數:");
for(int i = 0; i < args.length; i++) {
System.out.print(args[i]+ " ");
}
System.out.println();
 
Objectresult = null;
try{
//執行原來的方法之前記錄日誌 
 System.out.println(method.getName()+ "Method Start!");
//JVM通過這條語句執行原來的方法(反射機制) 
result= method.invoke(targetObject, args);
 
//執行原來的方法之後記錄日誌 
 System.out.println(method.getName()+ "Method End!");
}catch (Exception e) {
e.printStackTrace();
throwe;
}
//返回方法返回值給調用者 
returnresult;
}
 
}



該段代碼的執行流程


 

運行結果:

將要執行的方法:add

將要執行的方法中的參數:001tch

addMethod Start!

添加用戶成功

addMethod End!

 

      從上面的例子我們看出,你的任何對象的方法執行之前要加上記錄日誌的操作都是可以的.LogHandler自動去代理執行被代理對象UserManagerImp中的每一個方法,通過java.lang.reflect.InvocationHandler接口就把我們的代理對象和被代理對象解藕。

 

總結

上面簡單的介紹瞭如何用動態代理的方式實現AOP,主要是幫助大家理解動態代理AOP的大致思路,以便更好的使用AOP工具,並且把AOP應用到實際的面向對象開發中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章