Spring學(xué)習(xí)筆記(八、Spring對AspectJ的支持)

上一篇: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é)果:


Paste_Image.png

修改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é)果:


Paste_Image.png

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é)果:


Paste_Image.png

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é)果:


Paste_Image.png

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é)果:


Paste_Image.png

將業(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é)果:


Paste_Image.png

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é)果:


Paste_Image.png

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

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

Paste_Image.png

  1. 創(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é)果:


Paste_Image.png
  1. 創(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é)果:


Paste_Image.png

修改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é)果:


Paste_Image.png

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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容