面向切面編程(轉載)

Aspect Oriented Programming(面向切面編程),可以 通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一 添加功能的一種技術。

代碼分析  

我們直接從代碼入手吧,要實現以上的目標,我們可以使用一個動態代理類(Proxy),通過攔截一個對象的行爲並添加我們需要的功能來完成。Java中的java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口爲我們實現動態代理類提供了一個方案,但是該方案針對的對象要實現某些接口;如果針對的目的是類的話,cglib爲我們提供了另外一個實現方案。等下會說明兩者的區別。

一、接口的實現方案:

  1)首先編寫我們的業務接口(StudentInfoService.java):

    public interface StudentInfoService{

 

  void findInfo(String studentName);

 

  }

 

  及其實現類(StudentInfoServiceImpl.java):

 

  public class StudentInfoServiceImpl implements StudentInfoService{

 

  public void findInfo(String name){

 

  System.out.println("你目前輸入的名字是:"+name);

 

  }

 

  }

 

  2)現在我們需要一個日誌功能,在findInfo行爲之前執行並記錄其行爲,那麼我們就首先要攔截該行爲。在實際執行的過程中用一個代理類來替我們完成。Java中爲我們提供了實現動態代理類的方案:

 

  1'處理攔截目的的類(MyHandler.java)

 

  import org.apache.log4j.Logger;

 

  import java.lang.reflect.InvocationHandler;

 

  import java.lang.reflect.Proxy;

 

  import java.lang.reflect.Method;

 

  public class MyHandler implements InvocationHandler{

 

  private Object proxyObj;

 

  private static Logger log=Logger.getLogger(MyHandler.class);

 

  public Object bind(Object obj){

 

  this.proxyObj=obj;

 

  return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);

 

  }

 

  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{

 

  Object result=null;

 

  try{

 

  //請在這裏插入代碼,在方法前調用

 

  log.info("調用log日誌方法"+method.getName());

 

  result=method.invoke(proxyObj,args); //原方法

 

  //請在這裏插入代碼,方法後調用

 

  }catch(Exception e){

 

  e.printStackTrace();

 

  }

 

  return result;

 

  }

 

  }

 

  2'我們實現一個工廠,爲了方便我們使用該攔截類(AOPFactory.java):

 

  public class AOPFactory{

 

  private static Object getClassInstance(String clzName){

 

  Object obj=null;

 

  try{

 

  Class cls=Class.forName(clzName);

 

  obj=(Object)cls.newInstance();

 

  }catch(ClassNotFoundException cnfe){

 

  System.out.println("ClassNotFoundException:"+cnfe.getMessage());

 

  }catch(Exception e){

 

  e.printStackTrace();

 

  }

 

  return obj;

 

  }

 

  public static Object getAOPProxyedObject(String clzName){

 

  Object proxy=null;

 

  MyHandler handler=new MyHandler();

 

  Object obj=getClassInstance(clzName);

 

  if(obj!=null) {

 

  proxy=handler.bind(obj);

 

  }else{

 

  System.out.println("Can't get the proxyobj");

 

  //throw

 

  }

 

  return proxy;

 

  }

 

  }

 

  3)基本的攔截與其工廠我們都實現了,現在測試(ClientTest.java):

 

  public class ClientTest{

 

  public static void main(String[] args){

 

  StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl");

 

  studentInfo.findInfo("阿飛");

 

  }

 

  }

 

  輸出結果(看你的log4j設置):

 

  調用log日誌方法findInfo

 

  你目前輸入的名字是:阿飛

 

  這樣我們需要的效果就出來了,業務處理自己在進行,但是我們實現了日誌功能,而業務處理(StudentInfoService)根本不知道存在該行爲的。但是Java中提供的動態代理類的實現是針對實現了某些接口的類,如果沒有實現接口的話,不能創建代理類,看以上部分:

 

  return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);

 

  看到了沒有?obj.getClass().getInterfaces()要求實現了某些接口。以下提供哪些沒有實現接口的實現方案:

二、子類的實現方案。

  首先,請上網下CGLib的包,http://sourceforge.net/project/showfiles.php?group_id=56933 。設置好classpath路徑,CGLib與java標準庫提供的實現方案不同,cglib主要是基於實現類(如StudentInfoServiceImpl.java)擴展一個子類來實現。與Dynamic Proxy中的Proxy和InvocationHandler相對應,net.sf.cglib.proxy.Enhancer和MethodInterceptor在CGLib中負責完成代理對象創建和方法截獲處理,產生的是目標類的子類而不是通過接口來實現方法攔截的,Enhancer主要是用於構造動態代理子類來實現攔截,MethodInterceptor(擴展了Callback接口)主要用於實現around advice(AOP中的概念):

 

  1)我們的業務處理(StudentInfoServiceImpl.java):

 

  public class StudentInfoServiceImpl{

 

  public void findInfo(String name){

 

  System.out.println("你目前輸入的名字是:"+name);

 

  }

 

  }

 

  2)實行一個工具來處理日誌功能(AOPInstrumenter.java):

 

  import net.sf.cglib.proxy.MethodInterceptor;

 

  import net.sf.cglib.proxy.Enhancer;

 

  import net.sf.cglib.proxy.MethodProxy;

 

  import java.lang.reflect.Method;

 

  import org.apache.log4j.Logger;

 

  public class AOPInstrumenter implements MethodInterceptor{

 

  private Logger log=Logger.getLogger(AOPInstrumenter.class);

 

  private Enhancer enhancer=new Enhancer();

 

  public Object getInstrumentedClass(Class clz){

 

  enhancer.setSuperclass(clz);

 

  enhancer.setCallback(this);

 

  return enhancer.create();

 

  }

 

  public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable{

 

  log.info("調用日誌方法"+method.getName());

 

  Object result=proxy.invokeSuper(o,args);

 

  return result;

 

  }

 

  }

 

  3)我們來測試一下(AOPTest.java):

 

  public class AOPTest{

 

  public static void main(String[] args){

 

  AOPInstrumenter instrumenter=new AOPInstrumenter();

 

  StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class);

 

  studentInfo.findInfo("阿飛");

 

  }

 

  }

 

  輸出結果與以上相同。

 

  CGLib中爲實現以上目的,主要提供的類

 

  1)Enhancer:setCallback(Callback) ,setSuperclass(Class) ,create()返回動態子類Object

 

  2)MethodInterceptor必須實現的接口:intercept(Object,Method,Object[],MethodProxy)返回的是原方法調用的結果。和Proxy原理一樣。

三、AOP的基本概念:

  1)aspect(切面):實現了cross-cutting功能,是針對切面的模塊。最常見的是logging模塊,這樣,程序按功能被分爲好幾層,如果按傳統的繼承的話,商業模型繼承日誌模塊的話根本沒有什麼意義,而通過創建一個logging切面就可以使用AOP來實現相同的功能了。

 

  2)jointpoint(連接點):連接點是切面插入應用程序的地方,該點能被方法調用,而且也會被拋出意外。連接點是應用程序提供給切面插入的地方,可以添加新的方法。比如以上我們的切點可以認爲是findInfo(String)方法。

 

  3)advice(處理邏輯):advice是我們切面功能的實現,它通知程序新的行爲。如在logging裏,logging advice包括logging的實現代碼,比如像寫日誌到一個文件中。advice在jointpoint處插入到應用程序中。以上我們在MyHandler.java中實現了advice的功能

 

  4)pointcut(切點):pointcut可以控制你把哪些advice應用於jointpoint上去,通常你使用pointcuts通過正則表達式來把明顯的名字和模式進行匹配應用。決定了那個jointpoint會獲得通知。

 

  5)introduction:允許添加新的方法和屬性到類中。

 

  6)target(目標類):是指那些將使用advice的類,一般是指獨立的那些商務模型。比如以上的StudentInfoServiceImpl.

 

  7)proxy(代理類):使用了proxy的模式。是指應用了advice的對象,看起來和target對象很相似。

 

  8)weaving(插入):是指應用aspects到一個target對象創建proxy對象的過程:complie time,classload time,runtime

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