Spring框架的關鍵組件是面向方面編程(AOP)框架。面向方面的編程不僅打破程序邏輯分成不同的部分稱為所謂的擔憂。跨越多個點的應用程序的功能被稱為橫切關注點和這些橫切關注點是從應用程序的業務邏輯概念上區分開來。還有像日志記錄,審計,聲明性事務,安全性和高速緩存等方面的各種常見的好例子
模塊化的OOP中的關鍵單元是類,而在AOP中模塊化的單元則是切面。依賴注入可以幫助你從對方解耦應用程序對象和AOP可以幫助你從他們影響的對象分離橫切關注點。 AOP是一樣的編程語言如Perl,.NET,Java和其他觸發器。
Spring AOP模塊提供了攔截器攔截的應用程序,例如,執行一個方法時,可以之前或之后執行的方法添加額外的功能。
AOP術語:
在我們開始使用AOP之前,先熟悉AOP的概念和術語。這些條款是不特定于Spring,問題都是有關AOP。
建議的類型
Spring方面可以用5種下面提到的建議:
自定義方面實現
Spring基于XML模式的AOP
需要如下所述導入Spring AOP架構:
1
2
3
4
5
6
7
8
9
10
11
12
|
<? 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 > |
還需要在以下應用程序CLASSPATH中的AspectJ庫。這些庫可以在AspectJ的安裝'lib'目錄可用,可以從互聯網上下載它們。
- aspectjrt.jar
- aspectjweaver.jar
- aspectj.jar
聲明一個切面
一個方面是使用<aop:aspect>元素中聲明,并且支持bean是使用ref屬性如下參考:
1
2
3
4
5
6
7
8
9
|
<aop:config> <aop:aspect id= "myAspect" ref= "aBean" > ... </aop:aspect> </aop:config> <bean id= "aBean" class = "..." > ... </bean> |
這里的“aBean”將配置和依賴注入,就像任何其他的Spring bean,我們已經在前面的章節看到。
聲明一個切入點
一個切入點有助于確定與不同要執行的連接點的利息(即方法)。同時與XML架構基礎的配置工作,切入點將被定義如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
<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.yiibai下執行getName()方法在Student類:
1
2
3
4
5
6
7
8
9
10
11
12
|
<aop:config> <aop:aspect id= "myAspect" ref= "aBean" > <aop:pointcut id= "businessService" expression= "execution(* com.yiibai.Student.getName(..))" /> ... </aop:aspect> </aop:config> <bean id= "aBean" class = "..." > ... </bean> |
聲明建議
可以聲明任意五個建議的使用<aop:{ADVICE NAME}>元素下面給出一個<aop:aspect>內:
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
35
|
<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或不同的方法針對不同的建議。這些方法將被定義為縱橫模塊的一部分。
基于XML模式的AOP例
要理解上述關系到XML模式的AOP提到的概念,讓我們寫這將實現幾個建議的一個例子。
這里是Logging.java文件的內容。這實際上是縱橫模塊的一個示例,它定義的方法被調用的各個點。
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
35
36
37
|
package com.yiibai; public class Logging { /** * This is the method which I would like to execute * before a selected method execution. */ public void beforeAdvice(){ System.out.println( "Going to setup student profile." ); } /** * This is the method which I would like to execute * after a selected method execution. */ public void afterAdvice(){ System.out.println( "Student profile has been setup." ); } /** * This is the method which I would like to execute * when any method returns. */ public void afterReturningAdvice(Object retVal){ System.out.println( "Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised. */ public void AfterThrowingAdvice(IllegalArgumentException ex){ System.out.println( "There has been an exception: " + ex.toString()); } } |
以下是Student.java文件的內容:
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
|
package com.yiibai; public class Student { private Integer age; private String name; public void setAge(Integer age) { this .age = age; } public Integer getAge() { System.out.println( "Age : " + age ); return age; } public void setName(String name) { this .name = name; } public String getName() { System.out.println( "Name : " + name ); return name; } public void printThrowException(){ System.out.println( "Exception raised" ); throw new IllegalArgumentException(); } } |
以下是MainApp.java文件的內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.yiibai; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml" ); Student student = (Student) context.getBean( "student" ); student.getName(); student.getAge(); student.printThrowException(); } } |
以下是配置文件beans.xml文件:
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
|
<? 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 "> < aop:config > < aop:aspect id = "log" ref = "logging" > < aop:pointcut id = "selectAll" expression = "execution(* com.yiibai.*.*(..))" /> < aop:before pointcut-ref = "selectAll" method = "beforeAdvice" /> < aop:after pointcut-ref = "selectAll" method = "afterAdvice" /> < aop:after-returning pointcut-ref = "selectAll" returning = "retVal" method = "afterReturningAdvice" /> < aop:after-throwing pointcut-ref = "selectAll" throwing = "ex" method = "AfterThrowingAdvice" /> </ aop:aspect > </ aop:config > <!-- Definition for student bean --> < bean id = "student" class = "com.yiibai.Student" > < property name = "name" value = "Zara" /> < property name = "age" value = "11" /> </ bean > <!-- Definition for logging aspect --> < bean id = "logging" class = "com.yiibai.Logging" /> </ beans > |
創建源代碼和bean配置文件完成后,讓我們運行應用程序。如果一切順利,這將打印以下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Going to setup student profile. Name : Zara Student profile has been setup. Returning:Zara Going to setup student profile. Age : 11 Student profile has been setup. Returning:11 Going to setup student profile. Exception raised Student profile has been setup. There has been an exception: java.lang.IllegalArgumentException ..... other exception content |
解釋一下,上面定義<aop:pointcut>選擇所有的包com.yiibai下定義的方法。讓我們假設,想有一個特定的方法之前或之后執行意見,可以定義切入點與實際的類和方法的名稱取代星號(*)的切入點定義來縮小執行。下面是修改后的XML配置文件,以顯示概念:
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
|
<? 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 "> < aop:config > < aop:aspect id = "log" ref = "logging" > < aop:pointcut id = "selectAll" expression = "execution(* com.yiibai.Student.getName(..))" /> < aop:before pointcut-ref = "selectAll" method = "beforeAdvice" /> < aop:after pointcut-ref = "selectAll" method = "afterAdvice" /> </ aop:aspect > </ aop:config > <!-- Definition for student bean --> < bean id = "student" class = "com.yiibai.Student" > < property name = "name" value = "Zara" /> < property name = "age" value = "11" /> </ bean > <!-- Definition for logging aspect --> < bean id = "logging" class = "com.yiibai.Logging" /> </ beans > |
如果執行這些配置更改的示例應用程序,這將打印以下信息:
1
2
3
4
5
6
7
|
Going to setup student profile. Name : Zara Student profile has been setup. Age : 11 Exception raised ..... other exception content |
基于@AspectJ的AOP
@ AspectJ是指聲明方面的風格注釋的使用Java 5注釋普通的Java類。對@ AspectJ支持由包括您基于XML Schema的配置文件里面的下列元素啟用。
1
|
<aop:aspectj-autoproxy/> |
您還需要在以下應用程序的類路徑中的AspectJ庫。這些庫可以在AspectJ的安裝的'lib'目錄,可以從網上下載他們.
- aspectjrt.jar
- aspectjweaver.jar
- aspectj.jar
聲明一個切面
方面類是像任何其他普通的bean,并可能有方法和字段,就像任何其他類,但他們將被標注了@Aspect 如下:
1
2
3
4
5
6
7
8
|
package org.xyz; import org.aspectj.lang.annotation.Aspect; @Aspect public class AspectModule { } |
他們將在XML中進行配置像任何其他的bean,如下所示:
1
2
3
|
< bean id = "myAspect" class = "org.xyz.AspectModule" > <!-- configure properties of aspect here as normal --> </ bean > |
聲明一個切入點
一個切入點有助于確定與不同意見要執行的連接點的權益(即方法)。同時用@AspectJ的基礎配置工作,切入點聲明有兩個部分:
切入點表達式,決定哪些方法執行我們感興趣
一個切入點簽名的包含名字和任意數量的參數。該方法的實際主體是不相關的,實際上應為空。
下面的示例定義一個名為'businessService“切入點將匹配每個方法的可用包com.xyz.myapp.service下執行中的類:
1
2
3
4
|
import org.aspectj.lang.annotation.Pointcut; @Pointcut ( "execution(* com.xyz.myapp.service.*.*(..))" ) // expression private void businessService() {} // signature |
下面的示例定義一個名為'getName'切入點將匹配可用的軟件包com.yiibai下執行getName()方法在Student類:
1
2
3
4
|
import org.aspectj.lang.annotation.Pointcut; @Pointcut ( "execution(* com.yiibai.Student.getName(..))" ) private void getname() {} |
聲明建議
可以聲明任何使用 @{ADVICE-NAME} 注釋下面給出的五個建議。這假定已經定義了一個切入點簽名的方法的businessService():
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
|
@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(){
...
}
@AspectJ 基于AOP例子
要理解上述關系到@AspectJ的AOP的基礎概念提到,讓我們寫這將實現幾個建議的一個例子。
這里是Logging.java文件的內容。這實際上是方面模塊的一個示例,它定義的方法被調用的各個點。
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package com.yiibai; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Around; @Aspect public class Logging { /** Following is the definition for a pointcut to select * all the methods available. So advice will be called * for all the methods. */ @Pointcut ( "execution(* com.yiibai.*.*(..))" ) private void selectAll(){} /** * This is the method which I would like to execute * before a selected method execution. */ @Before ( "selectAll()" ) public void beforeAdvice(){ System.out.println( "Going to setup student profile." ); } /** * This is the method which I would like to execute * after a selected method execution. */ @After ( "selectAll()" ) public void afterAdvice(){ System.out.println( "Student profile has been setup." ); } /** * This is the method which I would like to execute * when any method returns. */ @AfterReturning (pointcut = "selectAll()" , returning= "retVal" ) public void afterReturningAdvice(Object retVal){ System.out.println( "Returning:" + retVal.toString() ); } /** * This is the method which I would like to execute * if there is an exception raised by any method. */ @AfterThrowing (pointcut = "selectAll()" , throwing = "ex" ) public void AfterThrowingAdvice(IllegalArgumentException ex){ System.out.println( "There has been an exception: " + ex.toString()); } } |
以下是Student.java文件的內容:
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
|
package com.yiibai; public class Student { private Integer age; private String name; public void setAge(Integer age) { this .age = age; } public Integer getAge() { System.out.println( "Age : " + age ); return age; } public void setName(String name) { this .name = name; } public String getName() { System.out.println( "Name : " + name ); return name; } public void printThrowException(){ System.out.println( "Exception raised" ); throw new IllegalArgumentException(); } } |
以下是MainApp.java文件的內容:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.yiibai; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MainApp { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "Beans.xml" ); Student student = (Student) context.getBean( "student" ); student.getName(); student.getAge(); student.printThrowException(); } } |
以下是配置文件beans.xml文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<? 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 "> < aop:aspectj-autoproxy /> <!-- Definition for student bean --> < bean id = "student" class = "com.yiibai.Student" > < property name = "name" value = "Zara" /> < property name = "age" value = "11" /> </ bean > <!-- Definition for logging aspect --> < bean id = "logging" class = "com.yiibai.Logging" /> </ beans > |
創建源程序和bean配置文件完成后,讓我們運行應用程序。如果一切順利,這將打印以下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
Going to setup student profile. Name : Zara Student profile has been setup. Returning:Zara Going to setup student profile. Age : 11 Student profile has been setup. Returning:11 Going to setup student profile. Exception raised Student profile has been setup. There has been an exception: java.lang.IllegalArgumentException ..... other exception content |