1、什么是AOP
AOP是Aspect Oriented Programming的縮寫,意思是面向方面編程,AOP實際是GoF設計模式的延續。
2、關于Spring AOP的一些術語:
A、切面(Aspect):在Spring AOP中,切面可以使用通用類或者在普通類中以@Aspect 注解(@AspectJ風格)來實現
B、連接點(Joinpoint):在Spring AOP中一個連接點代表一個方法的執行
C、通知(Advice):在切面的某個特定的連接點(Joinpoint)上執行的動作。通知有各種類型,其中包括"around"、"before”和"after"等通知。許多AOP框架,包括Spring,都是以攔截器做通知模型, 并維護一個以連接點為中心的攔截器鏈
D、切入點(Pointcut):定義出一個或一組方法,當執行這些方法時可產生通知,Spring缺省使用AspectJ切入點語法。
3、通知類型
A、前置通知(@Before):在某連接點(join point)之前執行的通知,但這個通知不能阻止連接點前的執行(除非它拋出一個異常)
B、返回后通知(@AfterReturning):在某連接點(join point)正常完成后執行的通知:例如,一個方法沒有拋出任何異常,正常返回
C、拋出異常后通知(@AfterThrowing):方法拋出異常退出時執行的通知
D、后通知(@After):當某連接點退出的時候執行的通知(不論是正常返回還是異常退出)
E、環繞通知(@Around):包圍一個連接點(join point)的通知,如方法調用。這是最強大的一種通知類型,環繞通知可以在方法調用前后完成自定義的行為,它也會選擇是否繼續執行連接點或直接返回它們自己的返回值或拋出異常來結束執行
4、@AspectJ風格的AOP配置
Spring AOP配置有兩種風格:
A、XML風格 = 采用聲明形式實現Spring AOP
B、AspectJ風格 = 采用注解形式實現Spring AOP
5、實例
切面類TestAspect
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
|
package com.spring.aop; /** * 切面 */ public class TestAspect { public void doAfter(JoinPoint jp) { System.out.println( "log Ending method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); } public Object doAround(ProceedingJoinPoint pjp) throws Throwable { long time = System.currentTimeMillis(); Object retVal = pjp.proceed(); time = System.currentTimeMillis() - time; System.out.println( "process time: " + time + " ms" ); return retVal; } public void doBefore(JoinPoint jp) { System.out.println( "log Begining method: " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName()); } public void doThrowing(JoinPoint jp, Throwable ex) { System.out.println( "method " + jp.getTarget().getClass().getName() + "." + jp.getSignature().getName() + " throw exception" ); System.out.println(ex.getMessage()); } private void sendEx(String ex) { //TODO 發送短信或郵件提醒 } } |
1
2
3
4
5
6
7
8
9
10
|
package com.spring.service; /** * 接口A */ public interface AService { public void fooA(String _msg); public void barA(); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
package com.spring.service; /** *接口A的實現類 */ public class AServiceImpl implements AService { public void barA() { System.out.println( "AServiceImpl.barA()" ); } public void fooA(String _msg) { System.out.println( "AServiceImpl.fooA(msg:" +_msg+ ")" ); } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package com.spring.service; /** * Service類B */ public class BServiceImpl { public void barB(String _msg, int _type) { System.out.println( "BServiceImpl.barB(msg:" +_msg+ " type:" +_type+ ")" ); if (_type == 1 ) throw new IllegalArgumentException( "測試異常" ); } public void fooB() { System.out.println( "BServiceImpl.fooB()" ); } } |
ApplicationContext
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
|
<? 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-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" default-autowire = "autodetect" > < aop:config > < aop:aspect id = "TestAspect" ref = "aspectBean" > <!--配置com.spring.service包下所有類或接口的所有方法--> < aop:pointcut id = "businessService" expression = "execution(* com.spring.service.*.*(..))" /> < aop:before pointcut-ref = "businessService" method = "doBefore" /> < aop:after pointcut-ref = "businessService" method = "doAfter" /> < aop:around pointcut-ref = "businessService" method = "doAround" /> < aop:after-throwing pointcut-ref = "businessService" method = "doThrowing" throwing = "ex" /> </ aop:aspect > </ aop:config > < bean id = "aspectBean" class = "com.spring.aop.TestAspect" /> < bean id = "aService" class = "com.spring.service.AServiceImpl" ></ bean > < bean id = "bService" class = "com.spring.service.BServiceImpl" ></ bean > </ beans > |
測試類AOPTest
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
|
public class AOPTest extends AbstractDependencyInjectionSpringContextTests { private AService aService; private BServiceImpl bService; protected String[] getConfigLocations() { String[] configs = new String[] { "/applicationContext.xml" }; return configs; } /** * 測試正常調用 */ public void testCall() { System.out.println( "SpringTest JUnit test" ); aService.fooA( "JUnit test fooA" ); aService.barA(); bService.fooB(); bService.barB( "JUnit test barB" , 0 ); } /** * 測試After-Throwing */ public void testThrow() { try { bService.barB( "JUnit call barB" , 1 ); } catch (IllegalArgumentException e) { } } public void setAService(AService service) { aService = service; } public void setBService(BServiceImpl service) { bService = service; } } |
運行結果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
log Begining method: com.spring.service.AServiceImpl.fooA AServiceImpl.fooA(msg:JUnit test fooA) log Ending method: com.spring.service.AServiceImpl.fooA process time: 0 ms log Begining method: com.spring.service.AServiceImpl.barA AServiceImpl.barA() log Ending method: com.spring.service.AServiceImpl.barA process time: 0 ms log Begining method: com.spring.service.BServiceImpl.fooB BServiceImpl.fooB() log Ending method: com.spring.service.BServiceImpl.fooB process time: 0 ms log Begining method: com.spring.service.BServiceImpl.barB BServiceImpl.barB(msg:JUnit test barB type:0) log Ending method: com.spring.service.BServiceImpl.barB process time: 0 ms log Begining method: com.spring.service.BServiceImpl.barB BServiceImpl.barB(msg:JUnit call barB type:1) log Ending method: com.spring.service.BServiceImpl.barB method com.spring.service.BServiceImpl.barB throw exception 測試異常 |