1. 程式人生 > >Spring 3 AOP總結 (包含註解式AOP定義, poincut及advice的一些定義方式)

Spring 3 AOP總結 (包含註解式AOP定義, poincut及advice的一些定義方式)

概念

AOP(Aspect Oriented Programming),即面向切面程式設計(也叫面向方面程式設計,面向方法程式設計)。其主要作用是,在不修改原始碼的情況下給某個或者一組操作新增額外的功能。像日誌記錄,事務處理,許可權控制等功能,都可以用AOP來“優雅”地實現,使這些額外功能和真正的業務邏輯分離開來,軟體的結構將更加清晰。AOP是OOP的一個強有力的補充。

術語

AOP的術語不太直觀,spring文件中也沒有給一個確切的定義,所以重在理解。

  • Join Point: Spring AOP中,join point就是一個方法。(通俗來講就是起作用的那個方法)。

  • Pointcut: 用來指定join point(通俗來講就是描述的一組符合某個條件的join point)。通常使用pointcut表示式來限定joint point,Spring預設使用AspectJ pointcut expression language。

  • Advice: 在join point上特定的時刻執行的操作,Advice有幾種不同型別,下文將會討論(通俗地來講就是起作用的內容和時間點)。

  • Introduction:給物件增加方法或者屬性。

  • Target object: Advice起作用的那個物件。

  • AOP proxy: 為實現AOP所生成的代理。在Spring中有兩種方式生成代理:JDK代理和CGLIB代理。

  • Aspect: 組合了Pointcut與Advice,在Spring中有時候也稱為Advisor。某些資料說Advisor是一種特殊的Aspect,其區別是Advisor只能包含一對pointcut和advice,但是aspect可以包含多對。AOP中的aspect可以類比於OOP中的class。

  • Weaving:將Advice織入join point的這個過程。

Advice的型別

  • Before advice:  執行在join point之前的advice,但是它不能阻止joint point的執行流程,除非丟擲了一個異常(exception)。

  • After returning advice: 執行在join point這個方法返回之後的advice。

  • After throwing advice: 執行在join point丟擲異常之後的advice。

  • After(finally) advice: 執行在join point返回之後或者丟擲異常之後的advice,通常用來釋放所使用的資源。

  • Around advice: 執行在join point這個方法執行之前與之後的advice。

兩種代理

Spring AOP是基於代理機制的。上文說到,Spring AOP通過JDK Proxy和CGLIB Proxy兩種方法實現代理。

如果target object沒有實現任何介面,那麼Spring將使用CGLIB來實現代理。CGLIB是一個開源專案,它是一個強大的,高效能,高質量的Code生成類庫,它可以在執行期擴充套件Java類與實現Java介面。

如果target object實現了一個以上的介面,那麼Spring將使用JDK Proxy來實現代理,因為Spring預設使用的就是JDK Proxy,並且JDK Proxy是基於介面的。這也是Spring提倡的面向介面程式設計。當然,你也可以強制使用CGLIB來進行代理,但是這樣可能會造成效能上的下降。

Pointcut expression

Pointcut通過pointcut expression來描述,有若干種限定詞。由於Pointcut的定義在Spring文件7.2.3 Declaring a pointcut中寫得比較詳細,所以在此不再贅述。

Spring AOP的使用

我們可以通過三種方式來使用Spring AOP,它們分別是:@Aspect-based(Annotation),Schema-based(XML),以及底層的Spring AOP API。

@Aspect-based (Annotation)

Annotaion是最常用的方式。

配置

首先,我們應該在配置檔案中增加對Annotation的支援。

假設我們的配置檔案是classpath下的applicationContext.xml,新增如下片段:

1
<aop:aspectj-autoproxy />

業務邏輯類

假設我們有一個UserManager類,這個類負責處理業務邏輯。類的定義如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserManager {
  /*這個方法需要一個引數*/
  public void addUser(String user) {
      System.out.println("addUser(String str) method is executed!");
  }
  public void deleteUser() {
      System.out.println("deleteUser() method is executed!");
  }
  /*這個方法返回一個字串*/
  public String getUser() {
      System.out.println("getUser() method is executed!");
      return "Hello";
  }
  /*這個方法丟擲一個異常*/
  public void editUser() throws Exception {
      throw new Exception("something is wrong.");
  }    
}

這是一個很普通的Java物件,看不出任何Spring AOP的痕跡,這也是Spring低侵入式設計的體現。

切面(Aspect)類

為了給業務邏輯增加額外功能,我們需要定義一個切面類,切面類裡包含了pointcut和advice。假設我們的切面類是ExampleAspect,程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Aspect
public class ExampleAspect {
  @Pointcut("execution(* com.psjay.example.spring.aop.*.*(..))")
  public void aPointcut() {
  }
  @Before("aPointcut()")
  public void beforeAdvice() {
      System.out.println("before advice is executed!");
  }
  @AfterReturning(pointcut = "aPointcut()", returning="r")
  public void afterReturningAdvice(String r) {
      if (r != null)
          System.out.println("after returning advice is executed! returning String is : " + r);
  }
  @After("aPointcut()")
  public void AfterAdvice() {
      System.out.println("after advice is executed!");
  }
  @After("aPointcut() && args(str)")
  public void AfterAdviceWithArg(String str) {
      System.out.println("after advice with arg is executed!arg is : " + str);
  }
  @AfterThrowing(pointcut="aPointcut()",throwing="e")
  public void afterThrowingAdvice(Exception e) {
      System.out.println("after throwing advice is executed!exception msg is : " + e.getMessage());
  }
}

在基於annotation的Spring AOP中,@Aspect用來標註切面類。@Pointcut標註一個空的方法,用來代表一個pointcut,這個方法必須是public的。@Pointcut註解括號內是pointcut expression,例子中的表示式表示com.psjay.example.spring.aop的所有方法都是join point。而@Before,@After等註解對應著幾種不同型別的Advice。被標註的方法就是一個Advice。@Advice註解括號內是一個pointcut。例子中的@afterReturningAdvice(),AfterAdviceWithArg()和afterThrowingAdvice()分別演示了Advice得到join point的返回值,Advice使用join point的引數,Advice使用join point丟擲的異常物件幾種操作。

不要忘了在Spring配置檔案中配置以上兩個類的“Bean”,這裡就不貼出具體程式碼了。

測試

測試類相對簡單,就是從Spring中拿出bean演示AOP的結果。測試類程式碼如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class Test {
  public static void main(String[] args) {
      ApplicationContext ctx  = new ClassPathXmlApplicationContext("applicationContext.xml");
      UserManager um =  ctx.getBean("userManager",UserManager.class);
      System.out.println("------ Case 1 --------");
      um.addUser("hey");
      System.out.println("------ Case 2 --------");
      try {
          um.editUs