1. 程式人生 > >Spring AOP從入門到放棄之概念以及Spring Boot AOP demo

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

AOP核心概念

1、橫切關注點

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

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

類是對物體特徵的抽象,切面就是對橫切關注點的抽象。
通知+切點
意思就是所有要被應用到增強(advice)程式碼的地方。(包括方法的方位資訊)

3、連線點(joinpoint)-》(被攔截的方法)

被攔截到的點,因為Spring只支援方法型別的連線點,所以在Spring中連線點指的就是被攔截的方法,實際上連線點還可以是欄位或者構造器

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

對連線點進行攔截的定義

5、通知(advice)-》(攔截後執行自己業務邏輯的那些部分)

所謂通知指的就是指攔截到連線點之後要執行的程式碼,通知分為前置、後置、異常、最終、環繞通知五類
這玩意也叫 增強
在邏輯層次上包括了我們抽取的公共邏輯和方位資訊。因為Spring只能方法級別的應用AOP,也就是我們常見的before,after,after-returning,after-throwing,around五種,意思就是在方法呼叫前後,異常時候執行我這段公共邏輯唄。

6、目標物件

代理的目標物件

7、織入(weave)

將切面應用到目標物件並導致代理物件建立的過程。
比如根據Advice中的方位資訊在指定切點的方法前後,執行增強。這個過程Spring 替我們做好了。利用的是CGLIB動態代理技術

8、引入(introduction)

在不修改程式碼的前提下,引入可以在執行期為類動態地新增一些方法或欄位

圖解

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

通知(Advice)型別

切面一共有五種通知

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 的支柱,使用場景非常廣泛。

1、日誌記錄

這裡寫圖片描述

2、許可權控制

3、事務

4、多資料來源讀寫切換

這裡寫圖片描述

原理

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

1、預設使用Java動態代理來建立AOP代理,這樣就可以為任何介面例項建立代理了

2、當需要代理的類不是代理介面的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB

spring boot 專案中定義使用自己的aop

1、引入jar

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

2、編寫切面

@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 [email protected]
 * <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 呼叫----");
    }


}

我的官網
我的部落格

相關推薦

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

AOP核心概念 1、橫切關注點 對哪些方法進行攔截,攔截後怎麼處理,這些關注點稱之為橫切關注點 2、切面(aspect)-》(通知+切點) 類是對物體特徵的抽象,切面就是對橫切關注點的抽象。 通知+切點 意思就是所有要被應用到增強(advic

spring-cloud】spring-cloud-入門到高可用-中

本篇帶大家從入門走向高可用,從屌絲走向高富帥... 1.先搭一個最簡單的專案,找點自信: 最簡單的spring-cloud專案需要一個註冊中心和兩個微服務,其他元件後面引入,這裡先不提, 註冊中心eurka程式碼入下: 先看依賴: 其中spring-boot版本採用2

Spring Cloud 入門到精通

課程介紹Spring Cloud 是一套完整的微服務解決方案,基於 Spring Boot 框架,準確的說,它不是一個框架,而是一個大的容器,它將市面上較好的微服務框架整合進來,從而簡化了開發者的程式碼量。本課程由淺入深帶領大家一步步攻克 Spring Cloud 各大模組,

Spring Boot2入門到實戰:統一異常處理

都說管理的精髓就是“制度管人,流程管事”。而所謂流程,就是對一些日常工作環節、方式方法、次序等進行標準化、規範化。且不論精不精髓,在技術團隊中,對一些通用場景,統一規範是必要的,只有步調一致,才能高效向前。如前後端互動協議,如本文探討的異常處理。   1. Spring

Spring+SpringMVC+MyBatis入門實踐(4)Spring + MyBatis整合

本過程基於Mybatis入門進行, 所以在開始之前希望你建庫,表結構,Mybatis的常用用法都已經熟練了。 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http

《SpringBoot入門放棄第(六)篇——Spring Security進行安全控制

一個好的系統,幾乎都離不開許可權控制。要實現訪問許可權控制的方式有多種多樣,可以通過AOP、攔截器實現,也可以使用Shiro框架。現在研究使用Spring Security。 O的K,先建立一個無需許可權的Web小例子。(本篇部落格接著之前寫的系列,已忽略環境配置,如pom.xml 的依賴等

《SpringBoot入門放棄第(十三)篇——使用@Async非同步呼叫,ThreadPoolTaskScheduler執行緒池,使用Future以及定義超時

建立 TaskPoolConfig 類,配置執行緒池: package com.test.util; import org.springframework.context.annotation.Bean; import org.springframework.cont

python爬蟲入門放棄(五) 正則的基本使用

語言 代碼例子 name 添加 iter ima 制表符 imp things 什麽是正則表達式 正則表達式是對字符串操作的一種邏輯公式,就是 事先定義好的一些特定字符、及這些特定字符的組合,組成一個“規則字符”,這個“規則字符” 來表達對字符的一種過濾邏輯。 正則並不是

python爬蟲入門放棄(六) BeautifulSoup庫的使用

src 表達 支持 正則表達 必須 這樣的 com 子節點 prettify 上一篇文章的正則,其實對很多人來說用起來是不方便的,加上需要記很多規則,所以用起來不是特別熟練,而這節我們提到的beautifulsoup就是一個非常強大的工具,爬蟲利器。 beautifulS

python爬蟲入門放棄(八) Selenium庫的使用

自動 .com 程序 png 都是 例子 等待 點擊 哪些 一、什麽是Selenium selenium 是一套完整的web應用程序測試系統,包含了測試的錄制(selenium IDE),編寫及運行(Selenium Remote Control)和測試的並行處理(Sele

Python爬蟲入門放棄(十一) Scrapy框架整體的一個了解

object 定義 roc encoding eth obi pipe pos 等等 這裏是通過爬取伯樂在線的全部文章為例子,讓自己先對scrapy進行一個整理的理解 該例子中的詳細代碼會放到我的github地址:https://github.com/pythonsite/

Python爬蟲入門放棄(十三) Scrapy框架的命令行詳解

directory xpath idf 成了 spider i386 名稱 4.2 不同的 這篇文章主要是對的scrapy命令行使用的一個介紹 創建爬蟲項目 scrapy startproject 項目名例子如下: localhost:spider zhaofan$ sc

pyton全棧開發入門放棄數據類型與變量

映射類 分隔 無法 列表常用操作 可用 內置 鏈式 無需 上一個 一.變量 1 什麽是變量之聲明變量 #變量名=變量值 age=18 gender1=‘male‘ gender2=‘female‘ 2 為什麽要有變量變量作用:“變”=>變化,“量”=>計量

Python爬蟲入門放棄(十八) Scrapy爬取所有知乎用戶信息(上)

user 說過 -c convert 方式 bsp 配置文件 https 爬蟲 爬取的思路 首先我們應該找到一個賬號,這個賬號被關註的人和關註的人都相對比較多的,就是下圖中金字塔頂端的人,然後通過爬取這個賬號的信息後,再爬取他關註的人和被關註的人的賬號信息,然後爬取被關註人

python全棧開發入門放棄函數基礎

*args 才會 沒有 pri 關鍵字 args none 結果 類型 1、為什麽要用函數#1.避免代碼重用#2.提高代碼的可讀性 2、函數的定義def 函數名(參數1,參數2): ‘‘‘函數註釋‘‘‘ print("函數體") return "返回值"

python全棧開發入門放棄裝飾器函數

def return app 不改變 art sdl 兩個 time() 必須 1、函數名可以當作函數的參數 1 import time 2 def timmer(func): 3 #函數名可以當做函數的參數 4 def inner(): 5

《Java入門放棄入門篇:spring中IOC的註入姿勢

java ioc spring IOC到底是個什麽東東呢?控制反轉(Inversion of Control,英文縮寫為IoC),其實就是這個東東。你隨便百度一下就會得到比較書面的解釋:通過引入實現了IoC模式的IoC容器,即可由IoC容器來管理對象的生命周期、依賴關系等,從而使得應用程序的配置和

python全棧開發入門放棄常用模塊和正則

imp 管理 gin idt 由於 說明 多次 mar style 什麽是模塊? 常見的場景:一個模塊就是一個包含了python定義和聲明的文件,文件名就是模塊名字加上.py的後綴。 但其實import加載的模塊分為四個通用類別:    1 使用python編寫

python全棧開發入門放棄socket網絡編程基礎

windows lis timeout 標準 網站 入門 make 取數 exce 網絡編程基礎 一 客戶端/服務器架構 1.硬件C/S架構(打印機) 2.軟件C/S架構   互聯網中處處是C/S架構   如黃色網站是服務端,你的瀏覽器是客戶端(B/S架構也是C/S架構的一

《Java入門放棄》JavaSE入門篇:面向對象概念(入門版)

soft java語法 luci text alt 女神 align 一起 ont 要知道什麽是面向對象,你首先要有個對象吧,所以······沒有對象的可以回家洗洗睡了· 好吧,前面是開玩笑,要說明什麽是面向對象,我們還是先 例子: 小呆:“小傻,你今天早餐