Spring AOP - 實現



Spring 支援@AspectJ 註解風格方法和基於schema的方法來實現自定義切面。

基於XML Schema

切面使用常規類以及基於XML的配置來實現。

要使用本節中描述的AOP名稱空間標籤,您需要匯入Spring AOP schema,如下所示:

<?xml version = "1.0" encoding = "UTF-8"?>
<beans xmlns = "http://www.springframework.org/schema/beans"
   
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" 
   xmlns:aop = "http://www.springframework.org/schema/aop"
   xsi:schemaLocation = "http://www.springframework.org/schema/beans
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

   <!-- bean definition & AOP specific configuration -->
</beans>

宣告切面

使用<aop:aspect>元素宣告一個切面,並使用ref屬性引用支援bean,如下所示。

<aop:config>
   <aop:aspect id = "myAspect" ref = "aBean">
   ...
   </aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
   ...
</bean>

這裡“aBean”將像您在前面章節中看到的那樣,被配置和依賴注入,就像任何其他Spring bean一樣。

宣告切點

切點有助於確定要與不同通知一起執行的感興趣的連線點(即方法)。在使用基於XML Schema的配置時,切點將如下定義:

<aop:config>
   <aop:aspect id = "myAspect" ref = "aBean">

   <aop:pointcut id = "businessService"
      expression = "execution(* com.xyz.myapp.service.*.*(..))"/>
      ...
   </aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
   ...
</bean>

以下示例定義了一個名為“businessService”的切點,該切點將匹配com.tutorialspoint包下Student類中getName()方法的執行。

<aop:config>
   <aop:aspect id = "myAspect" ref = "aBean">

   <aop:pointcut id = "businessService"
      expression = "execution(* com.tutorialspoint.Student.getName(..))"/>
   ...
   </aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
   ...
</bean>

宣告通知

您可以在<aop:aspect>內使用<aop:{通知名稱}>元素宣告五種通知中的任何一種,如下所示。

<aop:config>
   <aop:aspect id = "myAspect" ref = "aBean">
      <aop:pointcut id = "businessService"
         expression = "execution(* com.xyz.myapp.service.*.*(..))"/>

      <!-- a before advice definition -->
      <aop:before pointcut-ref = "businessService" 
         method = "doRequiredTask"/>

      <!-- an after advice definition -->
      <aop:after pointcut-ref = "businessService" 
         method = "doRequiredTask"/>

      <!-- an after-returning advice definition -->
      <!--The doRequiredTask method must have parameter named retVal -->
      <aop:after-returning pointcut-ref = "businessService"
         returning = "retVal"
         method = "doRequiredTask"/>

      <!-- an after-throwing advice definition -->
      <!--The doRequiredTask method must have parameter named ex -->
      <aop:after-throwing pointcut-ref = "businessService"
        throwing = "ex"
         method = "doRequiredTask"/>

      <!-- an around advice definition -->
      <aop:around pointcut-ref = "businessService" 
         method = "doRequiredTask"/>
   ...
   </aop:aspect>
</aop:config>

<bean id = "aBean" class = "...">
   ...
</bean>

您可以對不同的通知使用相同的doRequiredTask或不同的方法。這些方法將定義為切面模組的一部分。

基於@AspectJ

@AspectJ 指的是將切面宣告為用Java 5註解註釋的常規Java類的一種風格。透過在基於XML Schema的配置檔案中包含以下元素來啟用@AspectJ支援。

<aop:aspectj-autoproxy/>

宣告切面

切面類就像任何其他普通bean一樣,可以像任何其他類一樣擁有方法和欄位,不同之處在於它們將用@Aspect註解,如下所示。

package org.xyz;

import org.aspectj.lang.annotation.Aspect;

@Aspect
public class AspectModule {
}

它們將像任何其他bean一樣在XML中配置,如下所示。

<bean id = "myAspect" class = "org.xyz.AspectModule">
   <!-- configure properties of aspect here as normal -->
</bean>

宣告切點

切點有助於確定要與不同通知一起執行的感興趣的連線點(即方法)。在使用基於@AspectJ的配置時,切點宣告包含兩部分:

  • 一個切點表示式,它確定我們感興趣的哪些方法執行。

  • 一個切點簽名,包括一個名稱和任意數量的引數。方法的實際主體無關緊要,實際上應該為空。

以下示例定義了一個名為“businessService”的切點,該切點將匹配com.xyz.myapp.service包下類中每個方法的執行。

import org.aspectj.lang.annotation.Pointcut;

@Pointcut("execution(* com.xyz.myapp.service.*.*(..))") // expression 
private void businessService() {}  // signature

以下示例定義了一個名為“getname”的切點,該切點將匹配com.tutorialspoint包下Student類中getName()方法的執行。

import org.aspectj.lang.annotation.Pointcut;

@Pointcut("execution(* com.tutorialspoint.Student.getName(..))") 
private void getname() {}

宣告通知

您可以使用@{通知名稱}註解宣告五種通知中的任何一種,如下所示。這假設您已經定義了一個切點簽名方法businessService()。

@Before("businessService()")
public void doBeforeTask(){
   ...
}
@After("businessService()")
public void doAfterTask(){
   ...
}
@AfterReturning(Pointcut = "businessService()", returning = "retVal")
public void doAfterReturnningTask(Object retVal){
   // you can intercept retVal here.
   ...
}
@AfterThrowing(Pointcut = "businessService()", throwing = "ex")
public void doAfterThrowingTask(Exception ex){
   // you can intercept thrown exception here.
   ...
}
@Around("businessService()")
public void doAroundTask(){
   ...
}

您可以為任何通知內聯定義切點。以下是為前置通知定義內聯切點的示例。

@Before("execution(* com.xyz.myapp.service.*.*(..))")
public doBeforeTask(){
   ...
}
廣告