Spring AOP從入門到放棄之概念以及Spring Boot AOP demo

AOP核心概念

一、橫切關注點

對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之爲橫切關注點html

二、切面(aspect)-》(通知+切點)

類是對物體特徵的抽象,切面就是對橫切關注點的抽象。 通知+切點 意思就是全部要被應用到加強(advice)代碼的地方。(包括方法的方位信息)java

三、鏈接點(joinpoint)-》(被攔截的方法)

被攔截到的點,由於Spring只支持方法類型的鏈接點,因此在Spring中鏈接點指的就是被攔截的方法,實際上鍊接點還能夠是字段或者構造器git

四、切入點(pointcut)-》(描述攔截那些方法的部分)

對鏈接點進行攔截的定義github

五、通知(advice)-》(攔截後執行本身業務邏輯的那些部分)

所謂通知指的就是指攔截到鏈接點以後要執行的代碼,通知分爲前置、後置、異常、最終、環繞通知五類 這玩意也叫 加強 在邏輯層次上包括了咱們抽取的公共邏輯和方位信息。由於Spring只能方法級別的應用AOP,也就是咱們常見的before,after,after-returning,after-throwing,around五種,意思就是在方法調用先後,異常時候執行我這段公共邏輯唄。web

六、目標對象

代理的目標對象spring

七、織入(weave)

將切面應用到目標對象並致使代理對象建立的過程。 好比根據Advice中的方位信息在指定切點的方法先後,執行加強。這個過程Spring 替咱們作好了。利用的是CGLIB動態代理技術spring-boot

八、引入(introduction)

在不修改代碼的前提下,引入能夠在運行期爲類動態地添加一些方法或字段this

圖解

上面那一堆看不懂對嗎? 我也不太懂。 來看張圖 這裏寫圖片描述阿里雲

通知(Advice)類型

切面一共有五種通知spa

Before 某方法調用以前發出通知。

前置通知(Before advice) :在某鏈接點(JoinPoint)以前執行的通知, 但這個通知不能阻止鏈接點前的執行。在方法調用以前發出通

@Before("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void beforeTest() {
        System.out.println("執行 方法 以前 調用----");
    }

After 某方法完成以後發出通知

後通知(After advice) :當某鏈接點退出的時候執行的通知(不管是正常 返回仍是異常退出)。 不考慮方法運行的結果 。在方法調用以後發出通

@After("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterTest() {
        System.out.println();
        System.out.println("執行 方法 以後 調用----");
    }

After-returning 將通知放置在被通知的方法成功執行以後。

方法正常返回後,調用通知。在方法調用後,正常退出發出通

@AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterReturningTest() {
        System.out.println();
        System.out.println("執行 方法 AfterReturning 調用----");
    }

After-throwing 將通知放置在被通知的方法拋出異常以後。

拋出異常後通知(After throwing advice) : 在方法拋出異常退出時執行 的通知。在方法調用時,異常退出發出通

@AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterThrowingTest() {
        System.out.println();
        System.out.println("執行 方法 AfterThrowing 調用----");
    }

Around 通知包裹在被通知的方法的周圍知。

環繞通知(Around advice) :包圍一個鏈接點的通知,相似Web中Servlet 規範中的Filter的doFilter方法。能夠在方法的調用先後完成自定義的行爲,也能夠選擇不執行。在方法調用以前和以後發出通

@Around("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void aroundTest() {
        System.out.println();
        System.out.println("執行 方法 先後 調用----");
    }

執行結果

2017-10-27 19:51:51.605 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/aspecttest] is: -1
執行 方法 以前 調用----
JoinpointTest++++執行我正常流水線的業務邏輯

執行 方法 以後 調用----

執行 方法 AfterReturning 調用----
2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling
2017-10-27 19:51:51.622 DEBUG 15592 --- [io-8081-exec-10] o.s.web.servlet.DispatcherServlet        : Successfully completed request

切入點表達式

切入點指示符用來指示切入點表達式目的,在Spring AOP中目前只有執行方法這一個鏈接點,Spring AOP支持的AspectJ切入點指示符以下:

args()
定製join-point去匹配那些參數爲指定類型的方法的執行動做。

@args()
定製join-point去匹配那些參數被指定類型註解的方法的執行動做

execution()
開始匹配在其內部編寫的定製

this()
定製join-pont去匹配由AOP代理的Bean引用的指定類型的類。

target()
定製join-point去匹配特定的對象,這些對象必定是指定類型的類。

@target()
定製join-point去匹配特定的對象,這些對象要具備的指定類型的註解。

within()
定製join-point在必須哪個包中。

@within()
定製join-point在必須由指定註解標註的類中。

@annotation
定製鏈接點具備指定的註解。

只有execution用來執行匹配,其餘標誌符都只是爲了限制/定製他們所要匹配的鏈接點的位置。

命名及匿名切入點

這裏寫圖片描述

類型匹配語法

*:匹配任何數量字符。

..:匹配任何數量字符的重複,如在類型模式中匹配任何數量子包;而在方法參數模式中匹配任何數量參數。

+:匹配指定類型的子類型;僅能做爲後綴放在類型模式後邊。

例子

java.lang.String    匹配String類型;
  
java.*.String       匹配java包下的任何「一級子包」下的String類型;
					如匹配java.lang.String,但不匹配java.lang.ss.String  

java..*            匹配java包及任何子包下的任何類型;  
                   如匹配java.lang.String、java.lang.annotation.Annotation  
                  
java.lang.*ing     匹配任何java.lang包下的以ing結尾的類型;  

java.lang.Number+  匹配java.lang包下的任何Number的自類型;  
                   如匹配java.lang.Integer,也匹配java.math.BigInteger

詳細語法

註解? 修飾符? 返回值類型 類型聲明?方法名(參數列表) 異常列表?  

註解:可選,方法上持有的註解,如@Deprecated;

修飾符:可選,如public、protected;

返回值類型:必填,能夠是任何類型模式;「*」表示全部類型;

類型聲明:可選,能夠是任何類型模式;

方法名:必填,可使用「*」進行模式匹配;

參數列表:「()」表示方法沒有任何參數;「(..)」表示匹配接受任意個參數的方法,「(..,java.lang.String)」表示匹配接受java.lang.String類型的參數結束,且其前邊能夠接受有任意個參數的方法;「(java.lang.String,..)」 表示匹配接受java.lang.String類型的參數開始,且其後邊能夠接受任意個參數的方法;「(*,java.lang.String)」 表示匹配接受java.lang.String類型的參數結束,且其前邊接受有一個任意類型參數的方法;

異常列表:可選,以「throws 異常全限定名列表」聲明,異常全限定名列表若有多個以「,」分割,如throws java.lang.IllegalArgumentException, java.lang.ArrayIndexOutOfBoundsException。

匹配Bean名稱:可使用Bean的id或name進行匹配,而且可以使用通配符「*」;

組合切入點表達式

AspectJ使用 且(&&)、或(||)、非(!)來組合切入點表達式。在Schema風格下,因爲在XML中使用「&&」須要使用轉義字符「&&」來代替之,因此很不方便,所以Spring ASP 提供了and、or、not來代替&&、||、!。

通知參數

使用JoinPoint獲取:Spring AOP提供使用org.aspectj.lang.JoinPoint類型獲取鏈接點數據,任何通知方法的第一個參數均可以是JoinPoint(環繞通知是ProceedingJoinPoint,JoinPoint子類),固然第一個參數位置也能夠是JoinPoint.StaticPart類型,這個只返回鏈接點的靜態部分。

這裏寫圖片描述

運用場景

AOP 、IOC 作爲Spring 的支柱,使用場景很是普遍。

一、日誌記錄

個人另外一篇博客

這裏寫圖片描述

二、權限控制

##三、事務

四、多數據源讀寫切換

個人另外一篇博客

這裏寫圖片描述

原理

動態代理 Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關係也由IOC容器負責管理。所以,AOP代理能夠直接使用容器中的其它bean實例做爲目標,這種關係可由IOC容器的依賴注入提供。Spring建立代理的規則爲:

一、默認使用Java動態代理來建立AOP代理,這樣就能夠爲任何接口實例建立代理了

二、當須要代理的類不是代理接口的時候,Spring會切換爲使用CGLIB代理,也可強制使用CGLIB

spring boot 項目中定義使用本身的aop

一、引入jar

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

二、編寫切面

@Aspect // FOR AOP
@Order(-99) // 控制多個Aspect的執行順序,越小越先執行
@Component
public class AdviceTest {

@Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())")
    public void poincut(){

    }

    @Before("poincut()")
    public void beforeTest() {
        System.out.println("執行 方法 以前 調用----");
    }
}

這樣就完成了在spring boot 項目中定義使用本身的aop

注意代理模式

CGLIB動態代理技術 有時候你會發現 你的配置和我同樣,可是aop沒有生效,這頗有多是SpringMVC的配置的代理模式不對。

問題描述

方法裏的xxxService對象若是使用autowared注入,沒法啓動aspect, 可是 xxxService = ctx.getBean("xxxxx")獲取,是能夠啓用aspect的

緣由

這個時候 xxxService 並非注入進來的,即便有 @Autowired 註解,這時的註解沒有任何做用。 只有 Spring 生成的對象纔有 AOP 功能,由於 Spring 生成的代理對象纔有 AOP 功能。

解決方法

配置spring.aop.proxy-target-class=true

不錯的一個問題描述已經解決方法

文章代碼

/**
 * Created by chen on 2017/10/27.
 * <p>
 * Email 122741482@qq.com
 * <p>
 * Describe:
 */
@Service
public class JoinpointTest {

    public void JoinpointTest(){
        System.out.println("**********JoinpointTest*****************");
    }

    public void test(){
        System.out.println("JoinpointTest++++執行我正常流水線的業務邏輯");
    }
}
@Aspect // FOR AOP
@Order(-99) // 控制多個Aspect的執行順序,越小越先執行
@Component
public class AdviceTest {

@Pointcut(value="execution(* com.slife.java8.aspect.AspectTest.test())")
    public void poincut(){

    }

    @Before("poincut()")
    public void beforeTest() {
        System.out.println("執行 方法 以前 調用----");
    }

    @After("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterTest() {
        System.out.println();
        System.out.println("執行 方法 以後 調用----");
    }

    @Around("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void aroundTest() {
        System.out.println();
        System.out.println("執行 方法 先後 調用----");
    }



    @AfterReturning("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterReturningTest() {
        System.out.println();
        System.out.println("執行 方法 AfterReturning 調用----");
    }



    @AfterThrowing("execution(* com.slife.java8.aspect.AspectTest.test())")
    public void afterThrowingTest() {
        System.out.println();
        System.out.println("執行 方法 AfterThrowing 調用----");
    }


    @Before("execution(* com.slife.java8..*test*(..))")
    public void aspecttest1() {
        System.out.println();
        System.out.println("執行 方法aspecttest1  Before 調用----");
    }


}

阿里雲優惠券

個人官網 個人博客

個人官網http://guan2ye.com 個人CSDN地址http://blog.csdn.net/chenjianandiyi 個人簡書地址http://www.jianshu.com/u/9b5d1921ce34 個人githubhttps://github.com/javanan 個人碼雲地址https://gitee.com/jamen/ 點擊獲取阿里雲優惠券https://promotion.aliyun.com/ntms/act/ambassador/sharetouser.html?userCode=vf2b5zld&utm_source=vf2b5zld

相關文章
相關標籤/搜索