AspectJ的Aop和Spring的Aop

原文地址:https://blog.csdn.net/a128953ad/article/details/50509437

       

        AOP(Aspect OrientedProgramming, 面向切面/方面編程) 旨在從業務邏輯中分離出來橫切邏輯【eg:性能監控、日誌記錄、權限控制等】,提高模塊化,即通過AOP解決代碼耦合問題,讓職責更加單一。

 運用技術:

         SpringAOP使用了兩種代理機制,一種是基於JDK的動態代理,另一種是基於CGLib的動態代理,之所以需要兩種代理機制,很大程度上是因爲JDK本身只提供基於接口的代理,不支持類的代理。

 切面植入的方法:

     1、編譯期織入

     2、類裝載期織入

     3、動態代理織入---->在運行期爲目標類添加增強生成子類的方式,Spring AOP採用動態代理織入切面


流行的框架:

    AOP現有兩個主要的流行框架,即Spring AOP和Spring+AspectJ


   

二者的區別:

 1、織入的時期不同

    Spring Aop採用的動態織入,而Aspectj是靜態織入。靜態織入:指在編譯時期就織入,即:編譯出來的class文件,字節碼就已經被織入了。動態織入又分靜動兩種,靜則指織入過程只在第一次調用時執行;動則指根據代碼動態運行的中間狀態來決定如何操作,每次調用Target的時候都執行。有不清楚的同學,可以自己補下基礎的代理知識

 2、從使用對象不同

     Spring AOP的通知是基於該對象是SpringBean對象纔可以,而AspectJ可以在任何Java對象上應用通知。

    Spring AOP:如果你想要在通過this對象調用的方法上應用通知,那麼你必須使用currentProxy對象,並調用其上的相應方法;於此相似,如果你想要在某對象的方法上應用通知,那麼你必須使用與該對象相應的Spring bean

   AspectJ:使用AspectJ的一個間接侷限是,因爲AspectJ通知可以應用於POJO之上,它有可能將通知應用於一個已配置的通知之上。對於一個你沒有注意到這方面問題的大範圍應用的通知,這有可能導致一個無限循環。

      Spring AOP不同於大多數其他AOP框架。Spring AOP的目的並不是爲了提供最完整的AOP實現(雖然Spring AOP具有相當的能力);而是爲了要幫助解決企業應用中的常見問題,提供一個AOP實現與Spring IOC之間的緊密集成。由於Spring AOP是容易實現的,如果你計劃在Spring Beans之上將橫切關注點模塊化,Spring的這一目標將是要點之一。但同樣的目標也可能成爲一個限制,如果你用的是普通的Java對象而不是Spring beans,並基於此將橫切關注點模塊化的話。另一方面,AspectJ可用於基於普通Java對象的模塊化,但在實施之前需要良好的關於這個主題的知識。

   在決定使用哪種框架實現你的項目之前,有幾個要點可以幫助你做出合適的選擇(同樣適用於其他框架)。

     明確你在應用橫切關注點(cross-cutting concern)時(例如事物管理、日誌或性能評估),需要處理的是Spring beans還是POJO。如果正在開發新的應用,則選擇Spring AOP就沒有什麼阻力。但是如果你正在維護一個現有的應用(該應用並沒有使用Spring框架),AspectJ就將是一個自然的選擇了。爲了詳細說明這一點,假如你正在使用Spring AOP,當你想將日誌功能作爲一個通知(advice)加入到你的應用中,用於追蹤程序流程,那麼該通知(Advice)就只能應用在Spring beans的連接點(Joinpoint)之上

例子:在appbeans.xml中配置如下的切入點(pointcut),那麼當調用myServices bean的service方法時就將應用日誌通知(advice)。

<!—Configuration snippet in appbeans.xml -->
 
  <bean id="myServices" class="com.ashutosh.MyServicesImpl " />
 
  <aop:config>
 
    <aop:aspect id="loggingAspect" ref="logging">
 
       <aop:around method="log" pointcut="execution(public * *(..))"/>
 
   </aop:aspect>
 
 </aop:config --> 
 
// Java file calling service method
 
ApplicationContext beans =newClassPathXmlApplicationContext("appbeans.xml");
 
MyServices myServices = (MyServices) beans.getBean("myServices");
 
myServices.service(); // Logging advice applied here


看一下日誌通知將要被應用處的註釋,在這裏應用程序將記錄被調用方法的詳細信息。但是,當你在service()方法中調用同一個類中的其他方法時,如果你沒有使用代理對象,那麼日誌通知就不會被應用到這個方法調用上。

例如:

// MyServices service method
 
public void service() {
 
  performOperation();// Logging advice not going to apply here
 
}


如果你想要在通過this對象調用的方法上應用通知,那麼你必須使用currentProxy對象,並調用其上的相應方法。

// MyServices service method
 
public void service() {
 
  // Logging advice going to apply here
 
  ((MyServices) AopContext.currentProxy()).performOperation();
 
}


於此相似,如果你想要在某對象的方法上應用通知,那麼你必須使用與該對象相應的Spring bean。

public void service() {
 
  MyObject obj = new MyObject();
 
  Obj.performOperation();// Logging advice not going to apply here
 
}


如果你想要應用該通知,那麼上述代碼必須修改爲如下形式。

public void service() {
 
  MyObject obj = new MyObject();
 
 Obj.performOperation();// Logging advice not going to apply here
 
 ApplicationContext beans =newClassPathXmlApplicationContext("appbeans.xml");
 
 MyObject obj =(MyObject) beans.getBean("myObject");
 
 obj.performOperation()// Logging advice applied here
 
}


於此不同,使用“AspectJ”你可以在任何Java對象上應用通知,而不需要在任何文件中創建或配置任何bean。

另一個需要考慮的因素是,你是希望在編譯期間進行織入(weaving),還是編譯後(post-compile)或是運行時(run-time)。Spring只支持運行時織入。如果你有多個團隊分別開發多個使用Spring編寫的模塊(導致生成多個jar文件,例如每個模塊一個jar文件),並且其中一個團隊想要在整個項目中的所有Spring bean(例如,包括已經被其他團隊打包了的jar文件)上應用日誌通知(在這裏日誌只是用於加入橫切關注點的舉例),那麼通過配置該團隊自己的Spring配置文件就可以輕鬆做到這一點。之所以可以這樣做,就是因爲Spring使用的是運行時織入。

<!—Configuration -->
 
<bean id="myServices" class="com.ashutosh.MyServicesImpl " />
 
 <aop:config>
 
  <aop:aspect id="loggingAspect" ref="logging">
 
      <aop:around method="log" pointcut="execution(public * *(..))"/>
 
  </aop:aspect>
 
</aop:config -->


如果你使用AspectJ想要做到同樣的事情,你也許就需要使用acj(AspectJ編譯器)重新編譯所有的代碼並且進行重新打包。否則,你也可以選擇使用AspectJ編譯後(post-compile)或載入時(load-time)織入。

因爲Spring基於代理模式(使用CGLIB),它有一個使用限制,即無法在使用final修飾的bean上應用橫切關注點。因爲代理需要對Java類進行繼承,一旦使用了關鍵字final,這將是無法做到的。

例如,在Spring bean MyServicesImpl上使用關鍵字final,並配置一個“execution(public * *(..))”這樣的切入點,將導致運行時異常(exception),因爲Spring不能爲MyServicesImpl生成代理。

// Configuration file
 
<bean id="myServices" class="com.ashutosh.MyServicesImpl" />
 
//Java file
 
public final classMyServicesImpl {
 
  ---
 
}


在這種情況下,你也許會考慮使用AspectJ,其支持編譯期織入且不需要生成代理。

於此相似,在static和final方法上應用橫切關注點也是無法做到的。因爲Spring基於代理模式。如果你在這些方法上配置通知,將導致運行時異常,因爲static和final方法是不能被覆蓋的。在這種情況下,你也會考慮使用AspectJ,因爲其支持編譯期織入且不需要生成代理。

你一定希望使用一種易於實現的方式。因爲Spring AOP支持註解,在使用@Aspect註解創建和配置方面時將更加方便。而使用AspectJ,你就需要通過.aj文件來創建方面,並且需要使用ajc(Aspect編譯器)來編譯代碼。所以如果你確定之前提到的限制不會成爲你的項目的障礙時,使用Spring AOP。

使用AspectJ的一個間接侷限是,因爲AspectJ通知可以應用於POJO之上,它有可能將通知應用於一個已配置的通知之上。對於一個你沒有注意到這方面問題的大範圍應用的通知,這有可能導致一個無限循環。

例如,創建一個包含如下切入點的方面

public aspectLogging {
 
  Object around() : execution(public * * (..))
  Sysytem.out.println(thisJoinPoint.getSignature());
 
  return proceed();
 
}


在這種情況下,當proceed即將被調用時,日誌通知會被再次應用,這樣就導致了嵌套循環。

所以,如果你希望在Spring bean上採取比較簡單的方式應用橫切關注點時,並且這些bean沒有被標以final修飾符,同時相似的方法也沒有標以static或final修飾符時,就使用Spring AOP吧。相比之下,如果你需要在所提到的限制之上應用橫切關注點,或者要在POJO上應用關注點,那麼就使用AspectJ。你也可能選擇同時使用兩種方法,因爲Spring支持這樣。
 

轉載:https://blog.csdn.net/a128953ad/article/details/50509437

 

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