Spring思惟導圖,讓Spring再也不難懂(AOP 篇)

轉載自:
一、微信公衆號【Java思惟導圖
二、若谷先生html

1、什麼是 AOP ?

AOP(Aspect-Oriented Programming,面向方面編程),對 OOP(Object-Oriented Programming,面向對象編程)
clipboard.png
  • 【OOP與AOP】java

    • 概念spring

      • AOP(Aspect-Oriented Programming,面向方面編程)
      • OOP(Object-Oriented Programming,面向對象編程)
    • 方向express

      • OOP 定義從上到下的關係
      • AOP 定義從左到右的關係
  • 【兩個部分】編程

    • 核心關注點緩存

      • 業務處理的主要流程
    • 橫切關注點微信

      • 與業務主要流程關係不大的部分
      • 常常發生在覈心關注點的多處,而各處都是基本類似的功能
      • 如權限認證、日誌、事務處理

2、AOP 使用場景

一、AOP框架種類

  • AspectJ
  • JBoss AOP
  • Spring AOP

二、使用 AOP 場景

  • 性能監控:在方法調用先後記錄調用事件,方法執行太長或超時報警。
  • 緩存代理:緩存某方法的返回值,下次執行該方法時,直接從緩存裏獲取。
  • 軟件破解:使用AOP修改軟件的驗證類的判斷邏輯。
  • 記錄日誌:在方法執行先後記錄系統日誌。
  • 工做流系統:工做流系統須要將業務代碼和流暢引擎代碼混合在一塊兒執行,那麼可使用 AOP 將其分離,並動態掛載業務。
  • 權限驗證:方法執行前驗證是否有權限執行當前方法,沒有則拋出沒有權限執行異常,由業務代碼捕獲。

三、傳統編碼與AOP區別

clipboard.png

3、核心概念

clipboard.png
【術語】mybatis

  • 切面(Aspect):
    一個關注點的模塊化,這個關注點可能會橫切多個對象。
    事務管理是 J2EE 應用中一個關於橫切關注點的很好列子。
    在 Spring AOP 中,切面可使用基於模式或者基於 @Aspect 註解的方式來實現。
  • 鏈接點(Joinpoint):
    在程序執行過程當中某個特定的點,好比某方法調用的時候或者處理異常的時候。
    在 Spring AOP 中,一個鏈接點老是表示一個方法的執行。
  • 切入點(Pointcut):
    匹配鏈接點的斷言。通知和一個切入點表達式關聯,並在知足這個切入點的鏈接點上運行(例如:當執行某個特定名稱的方法時)。
    切入點表達式如何和鏈接點匹配是 AOP 的核心:Spring 缺省使用 Aspect 切入點語法。
  • 引入(Introduction):
    用來給一個類型聲明額外的方法或屬性(也被稱爲鏈接類型聲明(inter-type declaration))
    Spring 容許引入新的接口(以及一個對應的實現)到任何被代理的對象。例如,可使用引入來使一個 bean 實現 isModified 接口,以便簡化緩存機制。
  • 目標對象(Target Object):
    被一個或者多個切面所通知的對象,也被稱爲通知(advised)對象。
    既然 Spring AOP 是經過運行時代理實現的。這個對象永遠是一個被代理(proxied)對象。
  • AOP 代理(AOP Proxy):
    AOP 框架建立的對象,用來實現切面契約(例如,通知方法執行等等)。
    在 Spring 中,AOP 代理能夠是 JDK 動態代理或者 CGLIB 代理。
  • 織入(Weaving):
    把切面鏈接到其它的應用程序類型或者對象上,並建立一個被通知的對象。這些能夠在編譯時(例如使用 AspectJ 編譯器),類加載時和運行時完成。Spring 和其餘純 Java AOP 框架同樣,在運行時完成織入。
  • 通知(Advice):
    在切面的某個特定的鏈接點上執行的動做。其中包括「around」、「before」 和 「after」 等不一樣類型的通知。
    許多 AOP 框架(包括 Spring)都是以攔截器作通知模型,並維護一個以鏈接點爲中心的攔截器鏈。

【通知類型】app

  • 前置通知(Before advice):
    @Before
    在某鏈接點以前執行的通知,但這個通知不能阻止了鏈接點以前的執行流程(除非它拋出一個異常)。
  • 後置通知(After returning advice):
    @After
    在某鏈接點正常完成後執行的通知,例如,一個方法麼有拋出任何異常,正常返回。
  • 異常通知(After throwing advice):
    @After-returning
    在方法拋出異常退出時執行的通知。
  • 最終通知(After(finally)advice):
    @After-throwing
    當某鏈接點退出的時候執行的通知(不管是正常退出仍是異常退出);
  • 環繞通知(Around advice):
    @Around
    包圍一個鏈接點的通知,如方法調用,這是最強大的一種通知類型。

3、簡單例子

一、基於註解的方式

@Aspectj 
public class TransactionDemo {
    @Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))")
    public void point() { //... }
    
    @Before(value="point()")
    public void before() {
        System.out.println("transaction begin");
    }
    
    @AfterReturning(value = "point()")
    public void after() {
        System.out.println("transaction commit");
    }
    
    @Around("point()")
    public void around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("transaction begin");
        joinPoint.proceed();
        System.out.println("transaction commit");
    }
}

在 applicationContext.xml 中配置:框架

<aop:aspectj-autoproxy />
    <bean id="transactionDemo" class="com.yangxin.core.transaction.TransactionDemo" />

二、基於 XML 的方式

<aop:config>
    <aop:aspect ref="log">
        <aop:pointcut
                expression="(execution(* spring.ch3.topic1.Chief.*(..)))"
                id="chiefPointCut" />
        <aop:before method="washOven" pointcut-ref="chiefPointCut" />
        <aop:before method="prepare" pointcut-ref="chiefPointCut" />
        <aop:after method="after" pointcut-ref="chiefPointCut" />
    </aop:aspect>
</aop:config>

4、Spring AOP 原理

AOP 代理實際上是由 AOP 框架動態生成的一個對象,該對象可做爲目標對象使用。
AOP 代理包含了目標對象的所有方法,但 AOP 代理中的方法與目標對象的方法存在差別:AOP 方法在特定切入點添加了加強處理,並回調了目標對象的方法。
clipboard.png

Spring 的 AOP 代理由 Spring 的 IoC 容器負責生成、管理,其依賴關係也由 IoC 容器負責管理。所以,AOP 代理能夠直接使用容器中的其餘 Bean 實例做爲目標,這種關係可由 IoC 容器的依賴注入提供。

aop開發時,其中須要參與開發的只有 3 個部分:

  • 定義普通業務組件。
  • 定義切入點,一個切入點可能橫切多個業務組件。
  • 定義加強處理,加強處理就是在 AOP 框架爲普通業務組件織入的處理動做。

5、兩種動態代理方式

Spring默認採起的動態代理機制實現AOP,當動態代理不可用時(代理類無接口)會使用CGlib機制。

Spring提供了兩種方式來生成代理對象: JDKProxy和Cglib,具體使用哪一種方式生成由AopProxyFactory根據AdvisedSupport對象的配置來決定。默認的策略是若是目標類是接口,則使用JDK動態代理技術,不然使用Cglib來生成代理。

一、JDK 動態代理

  • JDK動態代理主要涉及到java.lang.reflect包中的兩個類:Proxy和InvocationHandler。InvocationHandler是一個接口,經過實現該接口定義橫切邏輯,並經過反射機制調用目標類的代碼,動態將橫切邏輯和業務邏輯編制在一塊兒。
  • Proxy利用InvocationHandler動態建立一個符合某一接口的實例,生成目標類的代理對象。

二、CGLib動態代理

  • CGLib全稱爲Code Generation Library,是一個強大的高性能,高質量的代碼生成類庫,能夠在運行期擴展Java類與實現Java接口,CGLib封裝了asm,能夠再運行期動態生成新的class。和JDK動態代理相比較:JDK建立代理有一個限制,就是隻能爲接口建立代理實例,而對於沒有經過接口定義業務方法的類,則能夠經過CGLib建立動態代理。

6、補充實例

clipboard.png

package springMVCmybatis.com.my.aop;
import org.aspectj.lang.annotation.Aspect; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Before; 
import org.aspectj.lang.annotation.Pointcut; 
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.springframework.core.annotation.Order;

@Aspect
@Order(3)
public class MyAopTest {
    @Pointcut("execution(* springMVCmybatis.addController.addEmp(..))")
    private void pointCutMethod() { //... }
    
    @Pointcut("execution(* springMVCmybatis.com.my.aop.UserServiceImp.*(..))") 
    private void testAOP() { //... }
    
    /**
    * 聲明前置通知 ,JoinPont是srpring提供的靜態變量,
    * 經過joinPoint參數能夠得到目標方法的類名,方法參數,方法名等信息,這個參數無關緊要。
    */
    @Before("pointCutMethod() || testAOP()") 
    public void doBefore(JoinPoint joinPoint) { 
        System.out.println("@Before:開始添加--order=3"); 
    } 
    
    /**
    * 聲明後置通知 ,若是result的類型與proceed執行的方法返回的參數類型不匹配那麼就不會執行這個方法
    */
    @AfterReturning(pointcut = "pointCutMethod()  || testAOP()", returning = "result") 
    public void doAfterReturning(String result) { 
        System.out.println("@AfterReturning:後置通知--order=3"); 
        System.out.println("---" + result + "---"); 
    } 
    
    /**
    * 聲明例外通知 
    */
    @AfterThrowing(pointcut = "pointCutMethod() || testAOP()", throwing = "e") 
    public void doAfterThrowing(Exception e) { 
        System.out.println("@AfterThrowing:例外通知--order=3"); 
        System.out.println(e.getMessage()); 
    }
    
    /**
    * 聲明最終通知
    */
    @After("pointCutMethod() || testAOP()") 
    public void doAfter() { 
        System.out.println("@After:最終通知--order=3"); 
    } 

    /**
    * 聲明環繞通知
    * 參數必須是ProceedingJoinPoint,經過該對象的proceed()方法來執行目標函數,
    * proceed()的返回值就是環繞通知的返回值,proceedingJoinPoint是個接口,
    * implement JoinPoint,因此也能夠得到目標函數的類名,方法名等參數。
    */
    @Around("pointCutMethod() || testAOP()") 
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
        System.out.println("@Around:進入方法---環繞通知--order=3"); 
        Object o = pjp.proceed(); 
        System.out.println("@Around:退出方法---環繞通知--order=3"); 
        return o; 
    }
}

7、切點表達式

一、方法簽名表達式

execution(<修飾符模式>?<返回類型模式><方法所在類的徹底限定名稱模式>(<參數模式>)<異常模式>?)
execution(modifiers-pattern? ret-type-pattern fully-qualified-class-name (param-pattern) throws-pattern?)   
對比方法的定義來記憶,一個java方法的所有定義方式能夠表示成下面的方式:
public String springMVCmybatic.com.my.aop.UserServiceImp(String a, int b) throw Exception{}
  • modifier-pattern?:表示方法的修飾符,無關緊要;對應的就是 public
  • ret-type-pattern:表示方法的返回值;對應的就是 String
  • fully-qualified-class-name 方法所在類的徹底限定名稱;對應的就是 springMVCmybatic.com.my.aop.UserServiceImp
  • param-pattern:表示方法的參數;對應的就是 String a, int b
  • throws-pattern:表示方法拋出的異常,無關緊要;對應的就是 throw Exception

二、&&,||,!(表達式之間能夠採用與,或,非的方式來過濾。)

@Around("pointCutMethod() || testAOP()") 
public Object doAround(ProceedingJoinPoint pjp) throws Throwable { 
    System.out.println("@Around:進入方法---環繞通知"); 
    Object o = pjp.proceed(); 
    System.out.println("@Around:退出方法---環繞通知"); 
    return o; 
}

8、多個切點的執行順序

上面的例子中,定義了order=3,從新建立一個切面,定義order=6,執行的結果是:
@Around:進入方法---環繞通知--order=3
@Before:開始添加--order=3
@Around:進入方法---環繞通知--order=6
@Before:開始添加--order=6
============執行業務方法findUser,查找的用戶是:張三=============
@Around:退出方法---環繞通知--order=6
@After:最終通知--order=6
@AfterReturning:後置通知--order=6
---張三---
@Around:退出方法---環繞通知--order=3
@After:最終通知--order=3
@AfterReturning:後置通知--order=3
---張三---
 
 
@Around:進入方法---環繞通知--order=3
@Before:開始添加--order=3
@Around:進入方法---環繞通知--order=6
@Before:開始添加--order=6
============執行業務方法addUser=============
@After:最終通知--order=6
@AfterThrowing:例外通知--order=6
null
@After:最終通知--order=3
@AfterThrowing:例外通知--order=3
null
 從結果中能夠看出order越小越先執行,執行完了以後就order越小就越後推出。總結爲下面的圖:
clipboard.png
相關文章
相關標籤/搜索