Spring 3 AOP 概念及完整示例


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); } @Around("pointCut()") public void around(ProceedingJoinPoint pjp) throws Throwable { System.out.println("around start.."); try { pjp.proceed(); } catch (Throwable ex) { System.out.println("error in around"); throw ex; } System.out.println("around end"); } @AfterThrowing(pointcut = "pointCut()", throwing = "error") public void afterThrowing(JoinPoint jp, Throwable error) { System.out.println("error:" + error); } }

SimpleAspect類中的第一個方法是pointCut()方法,此方法無任何執行內容,只有一個@Pointcut的注解,其他方法都會引用這個注解中指定的pointcut表達式。

其他幾個方法是各種Advice類型的方法實現,在這些方法中都可以通過JoinPoint實例來獲得方法執行的上下文信息,參數信息。需要注意AfterReturning和AfterThrowing,After三種不同Advice的執行順序。

有了spring框架和aspectj以及cglib的支持,我們只需要實現上面兩個類就可以使用spring aop的功能了,下面我們看下如何在spring的配置文件中配置spring aop。

<?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"        xmlns:context="http://www.springframework.org/schema/context"        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                            http://www.springframework.org/schema/context                            http://www.springframework.org/schema/context/spring-context-3.1.xsd">     <aop:aspectj-autoproxy />     <context:component-scan base-package="cn.outofmemory" />  </beans>

注意在beans節點中需要指定aop命名空間,一家chemaLocation地址,啟用aop只需要添加<aop:aspectj-autoproxy/>就可以了。<context:component-scan/>節點用來指定自動掃描bean的命名空間

最后我們要測試下aop的效果,需要新建一個App類作為程序的入口類。

package cn.outofmemory.spring_aop_aspect; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /**  * Hello world!  *  */ public class App  {     public static void main( String[] args )     {     ApplicationContext appContext = new ClassPathXmlApplicationContext("/appContext.xml");     PersonService personService = appContext.getBean(PersonService.class);     String personName = "Jim";     personService.addPerson(personName);     personService.deletePerson(personName);     personService.editPerson(personName);     ((ClassPathXmlApplicationContext)appContext).close();     } }

這個類中我們初始化了ApplicationContext,然后從中得到PersonService的實例,並執行其addPerson,deletePerson,editPerson方法,最后關閉ApplicationContext。

其執行結果如下:

before aspect executing
around start.. add person Jim after aspect executed around end afterReturning executed, return result is null ------------------------------------- before aspect executing around start.. delete person Jim after aspect executed around end afterReturning executed, return result is null ---------------------------------------- before aspect executing around start.. edit person Jim after aspect executed error in around error:java.lang.RuntimeException: edit person throw exception Exception in thread ...

從上面執行結果可以看到我們要執行的Before,around,after以及around,afterReturning,afterThrowing都正常的執行了。

 

AOP 配置詳解

http://blog.csdn.net/lipei1220/article/details/52152430

 

Spring AOP 完成日志記錄

http://hotstrong.iteye.com/blog/1330046

http://blog.csdn.net/baidu_25958185/article/details/43764467

http://www.oschina.net/code/snippet_1159320_49213

 

Spring Aop自定義注解攔截Controller實現日志管理

http://blog.csdn.net/itnik/article/details/38542373

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM