你知道面試必問的AOP嗎?通過Spring又如何實現(xiàn)呢?

Aspect Oriented Programing 面向切面編程,相比較 oop 面向?qū)ο缶幊虂碚f,Aop 關(guān)注的不再是程序代碼中某個類,某些方法,而 aop 考慮的更多的是一種面到面的切入,即層與層之間的一種切入,所以稱之為切面。聯(lián)想大家吃的漢堡(中間夾肉)。那么 aop 是怎么做到攔截整個面的功能呢?考慮學(xué)到的 servlet urlpattern /* 的配置,實際上也是 aop 的實現(xiàn) 。

Spring Aop 實現(xiàn)的方式

  • 注解 方式
  • XML 方式

案例實操

注解方式

jar 包坐標(biāo)引入

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

beans.xml 配置

添加命名空間

xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop.xsd

配置 Aop 代理

<aop:aspectj-autoproxy/>

編寫 aop 實現(xiàn)類

/**
* 聲明切面組件
*/
@Component
@Aspect
public class LogCut {
    /**
    * 定義切入點 匹配方法規(guī)則定義
    * 匹配規(guī)則表達(dá)式含義 攔截 com.xxx.service 包下 以及子包下 所有類的所有方法
    */
    @Pointcut("execution (* com.xxx.service..*.*(..))")
    public void cut(){}
    /**
    * 聲明前置通知 并將通知應(yīng)用到定義的切入點上
    * 目標(biāo)類方法執(zhí)行前 執(zhí)行該通知
    */
    @Before(value="cut()")
    public void before(){
        System.out.println("前置通知.....");
    }
    /**
    * 聲明返回通知 并將通知應(yīng)用到切入點上
    * 目標(biāo)類方法執(zhí)行完畢執(zhí)行該通知
    */
    @AfterReturning(value="cut()")
    public void afterReturning(){
        System.out.println("返回通知....");
    }
    /**
    * 聲明最終通知 并將通知應(yīng)用到切入點上
    * 目標(biāo)類方法執(zhí)行過程中是否發(fā)生異常 均會執(zhí)行該通知 相當(dāng)于異常中的 finally 
    */
    @After(value="cut()")
    public void after(){
        System.out.println("最終通知....");
    }
    /**
    * 聲明異常通知 并將通知應(yīng)用到切入點上
    * 目標(biāo)類方法執(zhí)行時發(fā)生異常 執(zhí)行該通知
    */
    @AfterThrowing(value="cut()",throwing="e")
    public void afterThrowing(Exception e){
        System.out.println("異常通知....方法執(zhí)行異常時執(zhí)行:"+e);
    }
    /**
    * 聲明環(huán)繞通知 并將通知應(yīng)用到切入點上
    * 方法執(zhí)行前后 通過環(huán)繞通知定義相應(yīng)處理
    */
    @Around(value="cut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("環(huán)繞前置...");
        System.out.println("環(huán)繞通知");
        System.out.println(pjp.getTarget()+"--"+pjp.getSignature());
        Object result=pjp.proceed();//執(zhí)行目標(biāo)對象方法
        System.out.println("環(huán)繞后置...");
        return result;
    } 
}

Aop 匹配方法規(guī)則表達(dá)式語言(簡要了解)

Aop 切入點表達(dá)式簡介

執(zhí)行任意公共方法:

execution(public *(..))

執(zhí)行任意的 set 方法

execution(* set*(..))

執(zhí)行 com.xxx.service 包下任意類的任意方法

execution(* com.xxx.service.*.*(..))

執(zhí)行 com.xxx.service 包 以及子包下任意類的任意方法

execution(* com.xxx.service..*.*(..))

xml 方式

配置切面、切入點、通知

<!-- aop 相關(guān)配置 -->
<aop:config>
    <!-- aop 切面配置 -->
    <aop:aspect ref="logCut">
        <!-- 定義 aop 切入點 -->
        <aop:pointcut expression="execution (* com.xxx.service..*.*(..))" 
        id="cut"/>
        <!-- 配置前置通知 指定前置通知方法名 并引用切入點定義 -->
        <aop:before method="before" pointcut-ref="cut"/>
        <!-- 配置返回通知 指定返回通知方法名 并引用切入點定義 -->
        <aop:after-returning method="afterReturning" pointcut-ref="cut"/>
        <!-- 配置異常通知 指定異常通知方法名 并引用切入點定義 -->
        <aop:after-throwing method="afterThrowing" throwing="e" pointcut-ref="cut"/>
        <!-- 配置最終通知 指定最終通知方法名 并引用切入點定義 -->
        <aop:after method="after" pointcut-ref="cut"/>
        <!-- 配置環(huán)繞通知 指定環(huán)繞通知方法名 并引用切入點定義 -->
        <aop:around method="around" pointcut-ref="cut"/>
    </aop:aspect>
</aop:config>

定義 bean

/**
* 聲明切面組件
*/
@Component
public class LogCut {
    public void before(){
        System.out.println("前置通知.....");
    }
    public void afterReturning(){
        System.out.println("返回通知....");
    } 
    public void after(){
        System.out.println("最終通知....");
    }
    public void afterThrowing(Exception e){
     System.out.println("異常通知....方法執(zhí)行異常時執(zhí)行:" + e);
    }
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("環(huán)繞前置...");
        System.out.println("環(huán)繞通知");
        System.out.println(pjp.getTarget()+"--"+pjp.getSignature());
        Object result=pjp.proceed();
        System.out.println("環(huán)繞后置...");
        return result;
    }
}

擴展

AOP 的基本概念

JoinPoint(連接點)【動態(tài)】

被攔截到的每個點,spring 中指被攔截到的每一個方法,spring aop 一個連接點即代表一個方法的執(zhí)行。

Pointcut(切入點)【靜態(tài)】

對連接點進(jìn)行攔截的定義(匹配規(guī)則定義 規(guī)定攔截哪些方法,對哪些方法進(jìn)行處理),spring 這塊有專門的表達(dá)式語言定義。

Advice(通知){重點}

攔截到每一個連接點即(每一個方法)前后所要做的操作

  • 前置通知(前置增強)--before() 執(zhí)行方法前通知
  • 返回通知(返回增強)--afterReturning 方法正常結(jié)束返回后的通知
  • 異常拋出通知(異常拋出增強)--afetrThrow()
  • 最終通知 --after 無論方法是否發(fā)生異常,均會執(zhí)行該通知
  • 環(huán)繞通知 --around 包圍一個連接點(join point)的通知,如方法調(diào)用。這是最強大的一種通知類型。 環(huán)繞通知可以在方法調(diào)用前后完成自定義的行為。它也會選擇是否繼續(xù)執(zhí)行連接點或直接返回它們自己的返回值或拋出異常來結(jié)束執(zhí)行

Aspect(切面)

切入點與通知的結(jié)合,決定了切面的定義,切入點定義了要攔截哪些類的 哪些方法,通知則定義了攔截方法后要做什么,切面則是橫切關(guān)注點的抽象,與類相似,類是對物體特征的抽象,切面則是橫切關(guān)注點抽象。

Target(目標(biāo)對象)

被代理的目標(biāo)對象

Weave(織入)

將切面應(yīng)用到目標(biāo)對象并生成代理對象的這個過程即為織入(過程)。

Introduction(引入)

在不修改原有應(yīng)用程序代碼的情況下,在程序運行期為類動態(tài)添加方法或者字段的過程稱為引入。

?著作權(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ù)。

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