AOP概念
AOP(Aspect Oriented Programming),即面向切面編程(也叫面向方面編程,面向方法編程)。其主要作用是,在不修改源代碼的情況下給某個或者一組操作添加額外的功能。像日志記錄,事務處理,權限控制等功能,都可以用AOP來“優雅”地實現,使這些額外功能和真正的業務邏輯分離開來,軟件的結構將更加清晰。AOP是OOP的一個強有力的補充。
AOP術語
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是基於代理機制的,通過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可以有下列方式來定義或者通過&& || 和!的方式進行組合.
args() @args() execution() this() target() @target() within() @within() @annotation
其中execution 是用的最多的,其格式為:
ret-type-pattern,name pattern, 和 parameters pattern是必須的.
- ret-type-pattern:可以為*表示任何返回值,全路徑的類名等.
- name-pattern:指定方法名,*代表所以,set*,代表以set開頭的所有方法.
- parameters pattern:指定方法參數(聲明的類型),(..)代表所有參數,(*)代表一個參數,(*,String)代表第一個參數為任何值,第二個為String類型.
舉例說明:
1) 任意公共方法的執行:
execution(public * *(..))
2) 任何一個以“set”開始的方法的執行:
execution(* set*(..))
3) AccountService 接口的任意方法的執行:
execution(* com.xyz.service.AccountService.*(..))
4) 定義在service包里的任意方法的執行:
execution(* com.xyz.service.*.*(..))
5) 定義在service包和所有子包里的任意類的任意方法的執行:
execution(* com.xyz.service..*.*(..))
6) 定義在pointcutexp包和所有子包里的JoinPointObjP2類的任意方法的執行:
execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))
7) pointcutexp包里的任意類.
within(com.test.spring.aop.pointcutexp.*)
8) pointcutexp包和所有子包里的任意類.
within(com.test.spring.aop.pointcutexp..*)
9) 實現了Intf接口的所有類,如果Intf不是接口,限定Intf單個類.
this(com.test.spring.aop.pointcutexp.Intf)
10) 帶有@Transactional標注的所有類的任意方法.
@within(org.springframework.transaction.annotation.Transactional) @target(org.springframework.transaction.annotation.Transactional)
11) 帶有@Transactional標注的任意方法.
@annotation(org.springframework.transaction.annotation.Transactional)
12) 參數帶有@Transactional標注的方法.
@args(org.springframework.transaction.annotation.Transactional)
13) 參數為String類型(運行是決定)的方法.
args(String)
完整示例:
首先新建一個maven項目,在項目的pom.xml中添加spring aop相關的依賴項:
如下是完整的pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.outofmemory</groupId> <artifactId>spring-aop-aspect</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>spring-aop-aspect</name> <url>http://maven.apache.org</url> <properties> <spring.version>3.1.1.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> </dependencies> </project>
在maven中我們引入了spring aop相關的依賴,aspectj相關的包有兩個,cglib是必須的。
下面我們在項目中新建一個Service類PersonService,它是業務代碼,是我們AOP注入的目標類:
package cn.outofmemory.spring_aop_aspect; import org.springframework.stereotype.Service; @Service public class PersonService { public void addPerson(String personName) { System.out.println("add person " + personName); } public boolean deletePerson(String personName) { System.out.println("delete person " + personName) ; return true; } public void editPerson(String personName) { System.out.println("edit person " + personName); throw new RuntimeException("edit person throw exception"); } }
PersonService類中定義了三個方法,addPerson方法無返回值,而deletePerson方法有返回值,而editPerson方法拋出運行時的異常。
下面我們看Aspect類:
package cn.outofmemory.spring_aop_aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class SimpleAspect { @Pointcut("execution(* cn.outofmemory.spring_aop_aspect.*Service*.*(..))") public void pointCut() { } @After("pointCut()") public void after(JoinPoint joinPoint) { System.out.println("after aspect executed"); } @Before("pointCut()") public void before(JoinPoint joinPoint) { //如果需要這里可以取出參數進行處理 //Object[] args = joinPoint.getArgs(); System.out.println("before aspect executing"); } @AfterReturning(pointcut = "pointCut()", returning = "returnVal") public void afterReturning(JoinPoint joinPoint, Object returnVal) { System.out.println("afterReturning executed, return result is " + returnVal); }