上一篇:Spring學(xué)習(xí)筆記(七、Spring AOP API)
一、AspectJ介紹與Pointcut注解應(yīng)用
1. AspectJ
- @AspectJ的風(fēng)格類似純java注解的普通java類。
- Spring可以使用AspectJ來做切入點分析。
- AOP的運行時仍舊是純的Spring AOP,對AspectJ的編譯器或者織入無依賴性。
2. Spring中配置AspectJ
- 對@AspectJ支持可以使用XML或Java風(fēng)格配置。
-
確保AspectJ的aspectjweaver.jar庫包含在應(yīng)用程序(版本1.6.8或更高版本)的classpath中。
Paste_Image.png
3. @Aspect注解
- AspectJ切面使用@Aspect注解配置,擁有@Aspect注解的任何bean,將被Spring自動識別并應(yīng)用。
- 用@Aspect注解的類可以有方法和字段,他們也可能包括切入點(pointcut)、通知(advice)、和引入(introduction)聲明。
- @Aspect注解是不能通過類路徑自動檢測發(fā)現(xiàn)的,所以需要配合使用@Component注釋或者在xml中配置bean。
- 一個類中的@Aspect注解標(biāo)識它為一個切面,并且將自己從自動代理中排除。
4. pointcut
- 一個切入點通過一個普通的方法定義來提供,并且切入點表達(dá)式使用@Pointcut注解,方法返回類型必須為void。
| 指示符 | 說明 |
|---|---|
| execution | 匹配方法執(zhí)行的連接點 |
| within | 限定匹配特定類型的連接點 |
| this | 匹配特定連接點的bean引用,是指定類型的實例的限制 |
| target | 限定匹配特定連接點的目標(biāo)對象是指定類型的實例 |
| args | 限定匹配特定連接點的參數(shù)是給定類型的實例 |
| @target | 限定匹配特定連接點的類執(zhí)行對象的具有給定類型的注解 |
| @args | 限定匹配特定連接點實際傳入的參數(shù)的類型具有給定類型的注解 |
| @within | 限定匹配到內(nèi)具有給定的注釋類型的連接點 |
| @annotation | 限定匹配特定連接點的主體具有給定的注解 |
創(chuàng)建一個切面類:AmberAspect
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
}
5. 組合pointcut
- 切入點表達(dá)式可以通過&&、||和!進(jìn)行組合,也可以通過名字引用切入點表達(dá)式。
-
通過組合,可以建立更加復(fù)雜的切入點表達(dá)式。
Paste_Image.png
6. 定義良好的pointcuts
- AspectJ是編譯器的AOP。
- 檢查代碼并匹配連接點與切入點的代價是昂貴的。
- 一個好的切入點應(yīng)該包括以下幾點:
- 選擇特定類型的連接點。如:execution、get、set、call、handler
- 確定連接點范圍,如:within、withincode
- 匹配上下文信息,如:this、target、@annotation
二、Advice定義及實例
1. Before advice
更新AmberAspect 切面類:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("execution(* com.amber.aop.biz.*Biz.*(..))")
public void before(){
System.out.println("前置通知!");
}
}
創(chuàng)建業(yè)務(wù)類AspectBiz :
package com.amber.aop.biz;
import org.springframework.stereotype.Service;
import test12.StringStore;
/**
* Created by amber on 2017/6/18.
* 業(yè)務(wù)類
*/
@Service
public class AspectBiz {
public String save(String args){
System.out.println("執(zhí)行AspectBiz的save方法,參數(shù):"+args);
return "Save Success";
}
}
applicationContext:
<context:component-scan base-package="com.amber.aop"></context:component-scan>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
測試類:
@Test
public void test17() {
AspectBiz aspectBiz=super.getBean("aspectBiz");
aspectBiz.save("淡雅如菊,溫潤如玉");
}
結(jié)果:

修改AmberAspect 切面類,將前置通知的表達(dá)式替換成同樣表達(dá)式的pointcut()方法:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("前置通知!");
}
}
結(jié)果:

2. After Returning Advice
- 有時候需要在通知體內(nèi)得到返回的實際值,可以使用@AfterReturning綁定返回值的形式。
更新AmberAspect 切面類,增加After Returning通知:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("前置通知!");
}
@AfterReturning(pointcut = "bizPointcut()",returning = "returnValue")
public void afterReturning(Object returnValue){
System.out.println("返回后通知,返回值為:"+returnValue);
}
}
結(jié)果:

3. After throwing advice
- 有時候需要在通知體內(nèi)得到返回的實際值,可以使用@AfterThrowing綁定返回值的形式。
更新AmberAspect 切面類,增加After Throwing通知:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("前置通知!");
}
@AfterReturning(pointcut = "bizPointcut()",returning = "returnValue")
public void afterReturning(Object returnValue){
System.out.println("返回后通知,返回值為:"+returnValue);
}
@AfterThrowing(pointcut = "bizPointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("異常后通知,異常為:"+e);
}
}
修改類:
package com.amber.aop.biz;
import org.springframework.stereotype.Service;
import test12.StringStore;
/**
* Created by amber on 2017/6/18.
* 業(yè)務(wù)類
*/
@Service
public class AspectBiz {
public String save(String args){
System.out.println("執(zhí)行AspectBiz的save方法,參數(shù):"+args);
throw new RuntimeException("Save failed");
//return "Save Success";
}
}
結(jié)果:

3. After(finally) advice
- 最終通知必須準(zhǔn)備處理正常和異常兩種返回情況,它通常用于釋放資源。
更新AmberAspect 切面類,增加After通知:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("前置通知!");
}
@AfterReturning(pointcut = "bizPointcut()",returning = "returnValue")
public void afterReturning(Object returnValue){
System.out.println("返回后通知,返回值為:"+returnValue);
}
@AfterThrowing(pointcut = "bizPointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("異常后通知,異常為:"+e);
}
@After("bizPointcut()")
public void after(){
System.out.println("后置通知!");
}
}
結(jié)果:

將業(yè)務(wù)類AspectBiz異常注釋:
package com.amber.aop.biz;
import org.springframework.stereotype.Service;
import test12.StringStore;
/**
* Created by amber on 2017/6/18.
* 業(yè)務(wù)類
*/
@Service
public class AspectBiz {
public String save(String args){
System.out.println("執(zhí)行AspectBiz的save方法,參數(shù):"+args);
// throw new RuntimeException("Save failed");
return "Save Success";
}
}
結(jié)果:

4. Around advice
- 環(huán)繞通知使用@Around注解來聲明,通知方法的第一個參數(shù)必須是ProceedingJoinPoint類型。
- 在通知內(nèi)部會調(diào)用ProceedingJoinPoint的proceed()方法會導(dǎo)致執(zhí)行真正的方法,傳入一個Object[]對象,數(shù)組中的值將被作為參數(shù)傳遞給方法。
更新AmberAspect ,增加Around通知:
package com.amber.aop.aspectj;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspect {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut(){
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut(){}
@Before("pointcut()")
public void before(){
System.out.println("前置通知!");
}
@AfterReturning(pointcut = "bizPointcut()",returning = "returnValue")
public void afterReturning(Object returnValue){
System.out.println("返回后通知,返回值為:"+returnValue);
}
@AfterThrowing(pointcut = "bizPointcut()",throwing = "e")
public void afterThrowing(Exception e){
System.out.println("異常后通知,異常為:"+e);
}
@After("bizPointcut()")
public void after(){
System.out.println("后置通知!");
}
@Around("bizPointcut()")
public Object around(ProceedingJoinPoint pjp)throws Throwable{
System.out.println("環(huán)繞通知,pjp.proceed();執(zhí)行前!");
Object obj=pjp.proceed();
System.out.println("環(huán)繞通知,pjp.proceed();執(zhí)行后! 返回值為:"+obj);
return obj;
}
}
結(jié)果:

三、給Advice傳遞參數(shù)
1. 給Advice傳遞參數(shù)

- 創(chuàng)建一個AmberAspectTwo切面類,增加帶參的前置通知,傳入普通參數(shù),將之前的AmberAspect類的@Aspect注釋掉:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspectTwo{
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut() {
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut() {
}
@Pointcut("pointcut()&&args(arg)")
public void before(String arg) {
System.out.println("前置通知,獲取參數(shù)為:" + arg);
}
}
結(jié)果:

- 創(chuàng)建一個自定義注解:
package com.amber.aop.aspectj;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by amber on 2017/6/20.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AmberMethod {
String value();
}
修改業(yè)務(wù)類方法,為其添加一個自定義注解:
package com.amber.aop.biz;
import com.amber.aop.aspectj.AmberMethod;
import org.springframework.stereotype.Service;
import test12.StringStore;
/**
* Created by amber on 2017/6/18.
* 業(yè)務(wù)類
*/
@Service
public class AspectBiz {
@AmberMethod("這是我自定義的注解")
public String save(String args){
System.out.println("執(zhí)行AspectBiz的save方法,參數(shù):"+args);
// throw new RuntimeException("Save failed");
return "Save Success";
}
}
更新AmberAspectTwo切面類,增加帶注解參數(shù)的后置通知:
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspectTwo {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut() {
}
@Pointcut("within(com.amber.aop.biz.*)")
public void bizPointcut() {
}
@Before("pointcut()&&args(arg)")
public void beforeWithArgs(String arg) {
System.out.println("前置通知,獲取參數(shù)為:" + arg);
}
@After("pointcut()&&@annotation(amberMethod)")
public void afterWithAnnotation(AmberMethod amberMethod) {
System.out.println("后置通知,獲取參數(shù)為:" + amberMethod.value());
}
}
結(jié)果:

修改AmberAspectTwo 切面類修改其后置通知的值引用bizPointcut()方法的組合切入點。
package com.amber.aop.aspectj;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* Created by amber on 2017/6/20.
*/
@Component
@Aspect
public class AmberAspectTwo {
@Pointcut("execution(* com.amber.aop.biz.*Biz.*(..))")
public void pointcut() {
}
@Pointcut("within(com.amber.aop.biz.*) && @annotation(amberMethod)")
public void bizPointcut(AmberMethod amberMethod) {
}
@Before("pointcut()&&args(arg)")
public void beforeWithArgs(String arg) {
System.out.println("前置通知,獲取參數(shù)為:" + arg);
}
@After("bizPointcut(amberMethod)")
public void afterWithAnnotation(AmberMethod amberMethod) {
System.out.println("后置通知,獲取參數(shù)為:" + amberMethod.value());
}
}
結(jié)果:

2. Advice的參數(shù)及泛型
-
Spring AOP可以處理泛型類的聲明和使用方法的參數(shù)。
Paste_Image.png
3. Advice參數(shù)名稱
-
通知和切入點有一個額外的“argName”屬性,它可以用來指定所注解的方法的參數(shù)名。
Paste_Image.png -
如果第一個參數(shù)是JoinPoint,ProceedingJoinPoint,JoinPoint.StaticPart,那么可以忽略它。
Paste_Image.png
3. Introductions
- 允許一個切面聲明一個通知對象實現(xiàn)指定接口,并且提供了一個接口實現(xiàn)類來代表這些對象。
-
Introduction使用@DeclareParents進(jìn)行注解,這個注解用來定義匹配的類型擁有一個新的parent。
Paste_Image.png
4. 切面實例化模型
- 這是一個高級主題
- “perthis” 切面通過指定@Aspect注解perthis子句實現(xiàn)。
- 每個獨立的service對象執(zhí)行時都會創(chuàng)建一個切面實例。
-
service對象的每個方法在第一次執(zhí)行的時候創(chuàng)建切面實例,切面在service對象失效的時候同時失效。
Paste_Image.png






