眾所周知,java是物件導向語言的有力代表,提到java我們就會立即想到物件導向,提到物件導向我們就會想到java。然而物件導向也並非完美無缺的,它更注重於對象階層方面的東西,對於如何更好的管理對象行為內部結構,還存在著些許不足。那麼我們如何使這個問題的得到更完美的解決呢?答案就是AOP。
AOP:Aspect-Oriented Programming。AOP是OOP的補充,是GOF的延續。我們知道設計模式是對於物件導向設計中經驗的總結,它孜孜不斷追求的就是調用者與被調用者之間的解耦。有了設計模式我們可以更有效利用物件導向的特性,使得整個軟體設計更加靈活、優雅。但是設計模式是基於物件導向的思想而形成的,更多的時候關注的是對象層次的東西,在解決對象行為內部問題方面卻有些不足。AOP的出現恰恰就是對物件導向思想做出了完美的補充。
說到AOP,我們就不得不來提一下軟體的縱向和橫向問題。從縱向結構來看就是我們軟體系統的各個模組,它主要負責處理我們的核心業務(例如商品訂購、購物車查看);而從橫向結構來看,我們幾乎每個系統又包含一些公用模組(例如許可權、日誌模組等)。這些公用模組分佈於我們各個核心業務之中(例如訂購和查看商品明細的過程都需要檢查使用者權限、記錄系統日誌等)。這樣一來不僅在開發過程中要處處關注公用模組的處理而且開發後維護起來也是十分麻煩。而有了AOP之後將應用程式中的商業邏輯同對其提供支援的泛型服務進行分離,使得開發人員可以更多的關注核心業務開發。
下面我們就以一個簡單的例子來看一下AOP吧!比如說,我們現在要開發的一個應用裡面有很多的業務方法,但是,我們現在要對這個方法的執行做全面監控,或部分監控.也許我們就會在要一些方法前去加上一條日誌記錄,我們寫個例子看看我們最簡單的解決方案
我們先寫一個介面IHello.java代碼如下:
public interface IHello{ /** *//** * 假設這是一個業務方法 *@param name */ void sayHello(String name); }
裡面有個方法,用於輸入"Hello" 加傳進來的姓名;我們去寫個類實現IHello介面
public class Helloimplements IHello{public void sayHello(String name){System.out.println("Hello" + name);}}
現在我們要為這個業務方法加上日誌記錄的業務,我們在不改變原代碼的情況下,我們會去怎麼做呢?也許,你會去寫一個類去實現IHello介面,並依賴Hello這個類.代碼如下:
public class HelloProxyimplements IHello{ private IHello hello; public HelloProxy(IHello hello){ this.hello= hello; } public void sayHello(String name){ Logger.logging(Level.DEBUGE,"sayHello method start."); hello.sayHello(name); Logger.logging(Level.INFO,"sayHello method end!"); }}
從上面的代碼我們可以看出,hello對象是被HelloProxy這個所謂的代理態所建立的.這樣,如果我們以後要把日誌記錄的功能去掉.那我們只要把得到hello對象的的具體實現改為Hello的就可以。上面的代碼 就是對AOP的最簡單的視線,但是我們接下來想,如果我們要在很多商務邏輯之前加日誌的話,那麼,我們是不是要去寫很多個HelloProxy這樣的類呢.沒錯,是的.其實也是一種很麻煩的事.在jdk1.3以後.jdk跟我們提供了一個API java.lang.reflect.InvocationHandler的類. 這個類可以讓我們在JVM調用某個類的方法時動態為些方法做些什麼事.讓我們把以上的代碼改一下來看看效果.
同樣,我們寫一個IHello的介面和一個Hello的實作類別.在介面中.我們定義兩個方法;代碼如下 :
IHello.java
package sinosoft.dj.aop.proxyaop; public interface IHello{ /** *//** * 業務處理A方法 *@param name */ void sayHello(String name); /** *//** * 業務處理B方法 *@param name*/void sayGoogBye(String name);}
Hello.java
package sinosoft.dj.aop.proxyaop; public class Helloimplements IHello{ public void sayHello(String name){ System.out.println("Hello" + name); } public void sayGoogBye(String name){ System.out.println(name+" GoodBye!"); }}
我們一樣的去寫一個代理類.只不過.讓這個類去實現java.lang.reflect.InvocationHandler介面,代碼如下:
package sinosoft.dj.aop.proxyaop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;import java.lang.reflect.Proxy; public class DynaProxyHelloimplements InvocationHandler{ /** *//** * 要處理的對象(也就是我們要在方法的前後加上商務邏輯的對象,如例子中的Hello)*/private Object delegate;/** *//** * 動態產生方法被處理過後的對象 (寫法固定) * *@param delegate *@param proxy *@return*/public Object bind(Object delegate){this.delegate= delegate;return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(),this.delegate .getClass().getInterfaces(),this); }/** *//** * 要處理的對象中的每個方法會被此方法送去JVM調用,也就是說,要處理的對象的方法只能通過此方法調用 * 此方法是動態,不是手動調用的*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{ Object result= null;try {//執行原來的方法之前記錄日誌 Logger.logging(Level.DEBUGE, method.getName()+ " Method end .");//JVM通過這條語句執行原來的方法(反射機制) result= method.invoke(this.delegate, args);//執行原來的方法之後記錄日誌 Logger.logging(Level.INFO, method.getName()+ " Method Start!"); } catch (Exception e){ e.printStackTrace(); }//返回方法傳回值給調用者 return result;} }
從上面的例子我們看出.只要你是採用面向介面編程,那麼,你的任何對象的方法執行之前要加上記錄日誌的操作都是可以的.他(DynaPoxyHello)自動去代理執行被代理對象(Hello)中的每一個方法,一個java.lang.reflect.InvocationHandler介面就把我們的代理對象和被代理對象解藕了