AOP

一、AOP介紹

AOP(Aspect Oriented Programming)意為:面向切面編程,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)编饺。AOP是OOP的延續(xù)壁酬,是軟件開(kāi)發(fā)中的一個(gè)熱點(diǎn),也是Spring框架中的一個(gè)重要內(nèi)容酣栈,是函數(shù)式編程的一種衍生范型。利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離汹押,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低矿筝,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率棚贾。

理解

u=2029981592,3062824930&fm=26&gp=0

1. 原理探究

AOP是Aspect Oriented Programming窖维,即面向切面編程。

那什么是AOP妙痹?

我們先回顧一下OOP:Object Oriented Programming铸史,OOP作為面向?qū)ο缶幊痰哪J剑@得了巨大的成功怯伊,OOP的主要功能是數(shù)據(jù)封裝琳轿、繼承和多態(tài)。

而AOP是一種新的編程方式,它和OOP不同崭篡,OOP把系統(tǒng)看作多個(gè)對(duì)象的交互挪哄,AOP把系統(tǒng)分解為不同的關(guān)注點(diǎn),或者稱(chēng)之為切面(Aspect)琉闪。

要理解AOP的概念迹炼,我們先用OOP舉例,比如一個(gè)業(yè)務(wù)組件BookService颠毙,它有幾個(gè)業(yè)務(wù)方法:

  • createBook:添加新的Book斯入;

  • updateBook:修改Book;

  • deleteBook:刪除Book吟秩。

對(duì)每個(gè)業(yè)務(wù)方法咱扣,例如,createBook(),除了業(yè)務(wù)邏輯,還需要安全檢查疚顷、日志記錄和事務(wù)處理,它的代碼像這樣:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n30" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class BookService {
public void createBook(Book book) {
securityCheck();
Transaction tx = startTransaction();
try {
// 核心業(yè)務(wù)邏輯
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
}
log("created book: " + book);
}
}</pre>

繼續(xù)編寫(xiě)updateBook()偏瓤,代碼如下:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n32" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class BookService {
public void updateBook(Book book) {
securityCheck();
Transaction tx = startTransaction();
try {
// 核心業(yè)務(wù)邏輯
tx.commit();
} catch (RuntimeException e) {
tx.rollback();
throw e;
}
log("updated book: " + book);
}
}</pre>

對(duì)于安全檢查、日志椰憋、事務(wù)等代碼厅克,它們會(huì)重復(fù)出現(xiàn)在每個(gè)業(yè)務(wù)方法中。使用OOP橙依,我們很難將這些四處分散的代碼模塊化证舟。

考察業(yè)務(wù)模型可以發(fā)現(xiàn),BookService關(guān)心的是自身的核心邏輯窗骑,但整個(gè)系統(tǒng)還要求關(guān)注安全檢查女责、日志、事務(wù)等功能创译,這些功能實(shí)際上“橫跨”多個(gè)業(yè)務(wù)方法抵知,為了實(shí)現(xiàn)這些功能,不得不在每個(gè)業(yè)務(wù)方法上重復(fù)編寫(xiě)代碼软族。

一種可行的方式是使用Proxy模式刷喜,將某個(gè)功能,例如立砸,權(quán)限檢查掖疮,放入Proxy中:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n36" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class SecurityCheckBookService implements BookService {
private final BookService target;

public SecurityCheckBookService(BookService target) {
this.target = target;
}

public void createBook(Book book) {
securityCheck();
target.createBook(book);
}

public void updateBook(Book book) {
securityCheck();
target.updateBook(book);
}

public void deleteBook(Book book) {
securityCheck();
target.deleteBook(book);
}

private void securityCheck() {
...
}
}</pre>

這種方式的缺點(diǎn)是比較麻煩,必須先抽取接口仰禽,然后氮墨,針對(duì)每個(gè)方法實(shí)現(xiàn)Proxy纺蛆。

另一種方法是吐葵,既然SecurityCheckBookService的代碼都是標(biāo)準(zhǔn)的Proxy樣板代碼规揪,不如把權(quán)限檢查視作一種切面(Aspect),把日志温峭、事務(wù)也視為切面猛铅,然后,以某種自動(dòng)化的方式凤藏,把切面織入到核心邏輯中奸忽,實(shí)現(xiàn)Proxy模式。

如果我們以AOP的視角來(lái)編寫(xiě)上述業(yè)務(wù)揖庄,可以依次實(shí)現(xiàn):

  1. 核心邏輯栗菜,即BookService;

  2. 切面邏輯蹄梢,即:

  3. 權(quán)限檢查的Aspect疙筹;

  4. 日志的Aspect;

  5. 事務(wù)的Aspect禁炒。

然后而咆,以某種方式,讓框架來(lái)把上述3個(gè)Aspect以Proxy的方式“織入”到BookService中幕袱,這樣一來(lái)暴备,就不必編寫(xiě)復(fù)雜而冗長(zhǎng)的Proxy模式。

AOP原理

如何把切面織入到核心邏輯中们豌?這正是AOP需要解決的問(wèn)題涯捻。換句話(huà)說(shuō),如果客戶(hù)端獲得了BookService的引用望迎,當(dāng)調(diào)用bookService.createBook()時(shí)障癌,如何對(duì)調(diào)用方法進(jìn)行攔截,并在攔截前后進(jìn)行安全檢查擂煞、日志混弥、事務(wù)等處理,就相當(dāng)于完成了所有業(yè)務(wù)功能对省。

在Java平臺(tái)上蝗拿,對(duì)于AOP的織入,有3種方式:

  1. 編譯期:在編譯時(shí)蒿涎,由編譯器把切面調(diào)用編譯進(jìn)字節(jié)碼哀托,這種方式需要定義新的關(guān)鍵字并擴(kuò)展編譯器,AspectJ就擴(kuò)展了Java編譯器劳秋,使用關(guān)鍵字aspect來(lái)實(shí)現(xiàn)織入仓手;

  2. 類(lèi)加載器:在目標(biāo)類(lèi)被裝載到JVM時(shí)胖齐,通過(guò)一個(gè)特殊的類(lèi)加載器,對(duì)目標(biāo)類(lèi)的字節(jié)碼重新“增強(qiáng)”嗽冒;

  3. 運(yùn)行期:目標(biāo)對(duì)象和切面都是普通Java類(lèi)呀伙,通過(guò)JVM的動(dòng)態(tài)代理功能或者第三方庫(kù)實(shí)現(xiàn)運(yùn)行期動(dòng)態(tài)織入。

最簡(jiǎn)單的方式是第三種添坊,Spring的AOP實(shí)現(xiàn)就是基于JVM的動(dòng)態(tài)代理剿另。由于JVM的動(dòng)態(tài)代理要求必須實(shí)現(xiàn)接口,如果一個(gè)普通類(lèi)沒(méi)有業(yè)務(wù)接口贬蛙,就需要通過(guò)CGLIB或者Javassist這些第三方庫(kù)實(shí)現(xiàn)雨女。

AOP技術(shù)看上去比較神秘,但實(shí)際上阳准,它本質(zhì)就是一個(gè)動(dòng)態(tài)代理氛堕,讓我們把一些常用功能如權(quán)限檢查、日志野蝇、事務(wù)等讼稚,從每個(gè)業(yè)務(wù)方法中剝離出來(lái)。

需要特別指出的是浪耘,AOP對(duì)于解決特定問(wèn)題乱灵,例如事務(wù)管理非常有用,這是因?yàn)榉稚⒃诟魈幍氖聞?wù)代碼幾乎是完全相同的七冲,并且它們需要的參數(shù)(JDBC的Connection)也是固定的痛倚。另一些特定問(wèn)題,如日志澜躺,就不那么容易實(shí)現(xiàn)蝉稳,因?yàn)槿罩倦m然簡(jiǎn)單,但打印日志的時(shí)候掘鄙,經(jīng)常需要捕獲局部變量耘戚,如果使用AOP實(shí)現(xiàn)日志,我們只能輸出固定格式的日志操漠,因此收津,使用AOP時(shí),必須適合特定的場(chǎng)景浊伙。

2. AOP常用實(shí)現(xiàn)方式

AOP常用的實(shí)現(xiàn)方式有兩種撞秋,

1、采用聲明的方式來(lái)實(shí)現(xiàn)(基于XML)嚣鄙,

2吻贿、是采用注解的方式來(lái)實(shí)現(xiàn)(基于AspectJ)。

3. AOP中一些比較重要的概念

  • 橫切關(guān)注點(diǎn):跨越應(yīng)用程序多個(gè)模塊的方法或功能哑子。即是舅列,與我們業(yè)務(wù)邏輯無(wú)關(guān)的肌割,但是我們需要關(guān)注的部分,就是橫切關(guān)注點(diǎn)帐要。如日志 , 安全 , 緩存 , 事務(wù)等等 ....

  • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">切面(ASPECT)</mark>:橫切關(guān)注點(diǎn) 被模塊化 的特殊對(duì)象把敞。即,它是一個(gè)類(lèi)宠叼。(把通知應(yīng)用到切入點(diǎn)的過(guò)程)

  • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">通知/增強(qiáng)(Advice)</mark>:切面必須要完成的工作先巴。即其爵,它是類(lèi)中的一個(gè)方法冒冬。說(shuō)的通俗點(diǎn),就是在指定切點(diǎn)上要干些什么摩渺。

    • 前置通知 Before

    • 后置通知 afterReturning

    • 環(huán)繞通知 Around

    • 異常通知 AfterThrowing

    • 最終通知 after

  • 目標(biāo)(Target):被通知對(duì)象简烤。

  • 代理(Proxy):向目標(biāo)對(duì)象應(yīng)用通知之后創(chuàng)建的對(duì)象。

  • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">切入點(diǎn)(PointCut)</mark>:切面通知 執(zhí)行的 “地點(diǎn)”的定義摇幻。(真正被增強(qiáng)的方方法)

  • <mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">連接點(diǎn)(JointPoint)</mark>:與切入點(diǎn)匹配的執(zhí)行點(diǎn)横侦。在Spring中就是某一個(gè)方法的執(zhí)行 。(簡(jiǎn)單理解:哪些方法可以被增強(qiáng)绰姻?)

注意:

在使用spring框架配置AOP的時(shí)候枉侧,不管是通過(guò)XML配置文件還是注解的方式都需要定義pointcut"切入點(diǎn)"。

<mark style="box-sizing: border-box; background: rgb(255, 255, 0); color: rgb(0, 0, 0);">execution([權(quán)限修飾符] [返回類(lèi)型] [類(lèi)全路徑] [方法名稱(chēng)] ([參數(shù)列表]))</mark>

例如定義切入點(diǎn)表達(dá)式 execution(* com.sample.service.impl..(..))

execution()是最常用的切點(diǎn)函數(shù)狂芋,其語(yǔ)法如下所示:

整個(gè)表達(dá)式可以分為五個(gè)部分:

(1)榨馁、execution(): 表達(dá)式主體。

(2)帜矾、第一個(gè)*號(hào):表示權(quán)限修飾符翼虫,可以是public等

(3)、包名:表示需要攔截的包名屡萤,后面的兩個(gè)句點(diǎn)表示當(dāng)前包和當(dāng)前包的所有子包珍剑,com.sample.service.impl包、子孫包下所有類(lèi)的方法死陆。

(4)招拙、第二個(gè)號(hào):表示類(lèi)名,號(hào)表示所有的類(lèi)措译。

(5)别凤、(..):最后這個(gè)星號(hào)表示方法名,號(hào)表示所有的方法瞳遍,后面括弧里面表示方法的參數(shù)闻妓,兩個(gè)句點(diǎn)表示任何參數(shù)。

二掠械、AOP在spring中的應(yīng)用

1. 基于XML配置的Spring AOP

1. 導(dǎo)入Aop織入依賴(lài)

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n110" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency></pre>

2. 添加業(yè)務(wù)接口

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n112" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public interface UserService {
/**
*功能描述: 執(zhí)行查詢(xún)

  • @author mfei
  • @date 2021/3/12
    */
    void select();
    }</pre>

3. 業(yè)務(wù)實(shí)現(xiàn)

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n114" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class UserServiceImpl implements UserService {

/**

  • 功能描述: 執(zhí)行查詢(xún)
  • @author mfei
  • @date 2022/2/12
    */
    public void select() {
    System.out.println("執(zhí)行查詢(xún)用戶(hù)的方法");
    }
    }</pre>

4. 創(chuàng)建切面類(lèi)

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n116" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class AopAspect {
/**

  • 前置通知:目標(biāo)方法調(diào)用之前執(zhí)行的代碼
  • @param jp 目標(biāo)方法
    /
    public void doBefore(JoinPoint jp){
    System.out.println("===========執(zhí)行前置通知============");
    }
    /
    *
  • 最終通知:目標(biāo)方法調(diào)用之后執(zhí)行的代碼(無(wú)論目標(biāo)方法是否出現(xiàn)異常均執(zhí)行)
  • 因?yàn)榉椒赡軙?huì)出現(xiàn)異常由缆,所以不能返回方法的返回值
  • @param jp 目標(biāo)方法
    */
    public void doAfter(JoinPoint jp){
    System.out.println("===========執(zhí)行最終通知============");
    }

/**

  • 環(huán)繞通知:目標(biāo)方法調(diào)用前后執(zhí)行的代碼注祖,可以在方法調(diào)用前后完成自定義的行為。
  • 包圍一個(gè)連接點(diǎn)(join point)的通知均唉。它會(huì)在切入點(diǎn)方法執(zhí)行前執(zhí)行同時(shí)方法結(jié)束也會(huì)執(zhí)行對(duì)應(yīng)的部分是晨。
  • 主要是調(diào)用proceed()方法來(lái)執(zhí)行切入點(diǎn)方法,來(lái)作為環(huán)繞通知前后方法的分水嶺舔箭。
  • 環(huán)繞通知類(lèi)似于動(dòng)態(tài)代理的全過(guò)程:ProceedingJoinPoint類(lèi)型的參數(shù)可以決定是否執(zhí)行目標(biāo)方法罩缴。
  • 而且環(huán)繞通知必須有返回值,返回值即為目標(biāo)方法的返回值
  • @param pjp 目標(biāo)方法
  • @return 返回值
  • @throws Throwable 異常接口
    /
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
    System.out.println("======執(zhí)行環(huán)繞通知開(kāi)始=========");
    // 調(diào)用方法的參數(shù)
    Object[] args = pjp.getArgs();
    // 調(diào)用的方法名
    String method = pjp.getSignature().getName();
    // 獲取目標(biāo)對(duì)象
    Object target = pjp.getTarget();
    // 執(zhí)行完方法的返回值
    // 調(diào)用proceed()方法层扶,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
    Object result=pjp.proceed();
    System.out.println("輸出,方法名:" + method + ";目標(biāo)對(duì)象:" + target + ";返回值:" + result);
    System.out.println("======執(zhí)行環(huán)繞通知結(jié)束=========");
    return result;
    }
    @AfterThrowing("execution(
    com.shida.dao.service.UserServiceImpl.*(..))")
    public void doAfterThrowing(){
    System.out.println("======在異常之后執(zhí)行=========");
    }
    }</pre>

5. spring配置

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n118" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<bean id="userService" class="com.shida.service.impl.UserServiceImpl"/>


<bean id="aspectBean" class="com.shida.aspect.AopAspect" />
<aop:config>
<aop:aspect ref="aspectBean">
<aop:pointcut id="pointcut" expression="execution(* com.shida.service.impl.UserServiceImpl..*(..))"/>
<aop:before method="doBefore" pointcut-ref="pointcut"/>
<aop:after method="doAfter" pointcut-ref="pointcut" />
<aop:around method="doAround" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>

</beans>
</pre>

6. 測(cè)試類(lèi)

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n120" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"> @Test
public void testAop(){
ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.select();
}</pre>

image-20210312105555314

2. 基于注解實(shí)現(xiàn)

采用注解來(lái)做aop, 主要是將寫(xiě)在spring 配置文件中的連接點(diǎn)寫(xiě)到注解里面箫章。

業(yè)務(wù)接口和業(yè)務(wù)實(shí)現(xiàn)與上邊一樣,具體切面類(lèi)如下:

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n125" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">package com.shida.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**

  • Description:
  • @author mfei
  • @date 2022/2/12
    /
    @Aspect
    public class AnnotationAopAspect {
    @Before("execution(
    com.shida.service.impl.UserServiceImpl.
    (..))")
    public void doBefore(){
    System.out.println("---------方法執(zhí)行前---------");
    }

@After("execution(* com.shida.service.impl.UserServiceImpl.*(..))")
public void doAfter(){
System.out.println("===========執(zhí)行最終通知============");
}

@Around("execution(* com.shida.service.impl.UserServiceImpl.*(..))")
public Object doAround(ProceedingJoinPoint jp) throws Throwable {
System.out.println("======執(zhí)行環(huán)繞通知開(kāi)始=========");
// 調(diào)用方法的參數(shù)
Object[] args = jp.getArgs();
// 調(diào)用的方法名
String method = jp.getSignature().getName();
// 獲取目標(biāo)對(duì)象
Object target = jp.getTarget();
// 執(zhí)行完方法的返回值
// 調(diào)用proceed()方法镜会,就會(huì)觸發(fā)切入點(diǎn)方法執(zhí)行
Object result=jp.proceed();
System.out.println("輸出,方法名:" + method + ";目標(biāo)對(duì)象:" + target + ";返回值:" + result);
System.out.println("======執(zhí)行環(huán)繞通知結(jié)束=========");
return result;
}
}</pre>

spring配置

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="xml" cid="n127" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;"><beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="com.shida.dao"/>


<bean id="hello" class="com.shida.entity.Hello">
<property name="name" value="Spring"/>
</bean>

<bean id="userService" class="com.shida.service.impl.UserServiceImpl"/>











<bean id="annotationPointcut" class="com.shida.aspect.AnnotationPointcut"/>


<aop:aspectj-autoproxy/>

</beans></pre>

注:若有兩個(gè)增強(qiáng)類(lèi)對(duì)同一方法進(jìn)行增強(qiáng)檬寂,可以設(shè)置@Order(1) ,數(shù)字越小,執(zhí)行順序越提前

Aop底層實(shí)現(xiàn):動(dòng)態(tài)代理

1.動(dòng)態(tài)代理:有接口的情況

JDK動(dòng)態(tài)代理實(shí)現(xiàn)戳表,創(chuàng)建接口實(shí)現(xiàn)類(lèi)的代理對(duì)象桶至,增強(qiáng)類(lèi)的方法

底層實(shí)現(xiàn)原理:implements

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n133" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public interface UserDao {
/**
*功能描述: 獲取用戶(hù)信息

  • @author mfei
  • @date 2022/2/25
    */
    void getUser();
    }</pre>

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n134" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class UserDaoImpl implements UserDao {

@Override
public void getUser() {
System.out.println("獲取到了用戶(hù)信息");
}
}</pre>

jdk動(dòng)態(tài)代理Proxy

Java 8 中文版 - 在線(xiàn)API中文手冊(cè) - 碼工具 (matools.com)

image-20220227113032721

參數(shù)理解:

interfaces 增強(qiáng)方法所在的類(lèi):這個(gè)類(lèi)實(shí)現(xiàn)的接口,支持多個(gè)接口

h 實(shí)現(xiàn)接口InvocationHandler匾旭,創(chuàng)建代理對(duì)象镣屹,寫(xiě)增強(qiáng)的方法

  1. 創(chuàng)建接口,實(shí)現(xiàn)方法

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n144" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public interface UserDao {

    int add(int a, int b);

    String getUser(String id);

    }</pre>

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n145" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
    return a+b;
    }

    @Override
    public String getUser(String id) {
    return id;
    }
    }</pre>

  2. 使用Proxy類(lèi)創(chuàng)建接口的代理對(duì)象

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n148" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class JDKProxy {

    public static void main(String[] args) {
    Class[] interfaces = {UserDao.class};
    // 創(chuàng)建接口實(shí)現(xiàn)類(lèi)
    Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
    // 匿名內(nèi)部類(lèi)
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    return null;
    }
    });

    }</pre>

    不適用匿名內(nèi)部類(lèi)

    完整代碼:

    <pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n151" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit;">public class JDKProxy {

    public static void main(String[] args) {
    Class[] interfaces = {UserDao.class};
    UserDaoImpl userDao = new UserDaoImpl();
    UserDao user = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UseDaoProxy(userDao));
    int add = user.add(1, 2);
    System.out.println(add);
    }
    }
    class UseDaoProxy implements InvocationHandler{
    private Object obj;
    public UseDaoProxy(Object obj) {
    this.obj = obj;
    }

 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 System.out.println("方法之前執(zhí)行"+method.getName()+"傳遞的參數(shù)"+ Arrays.toString(args));
 Object invoke = method.invoke(obj, args);
 System.out.println(invoke);
 System.out.println(obj);
 return invoke;
 }
}</pre>

2.沒(méi)有接口情況的動(dòng)態(tài)代理

底層實(shí)現(xiàn)原理:extends

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n155" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">/**

  • Description:
  • @author mfei
  • Date: 2022/2/25 9:36
    **/
    public class UserService {

public void getUser(){
System.out.println("獲取到了用戶(hù)信息");
}
}</pre>

<pre class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" lang="java" cid="n156" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial;">public class PersonService extends UserService {

@Override
public void getUser() {
super.getUser();
// 增強(qiáng)的邏輯
}
}</pre>

通過(guò)CGLIB動(dòng)態(tài)代理

實(shí)現(xiàn)MethodInterceptor接口

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末价涝,一起剝皮案震驚了整個(gè)濱河市女蜈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌飒泻,老刑警劉巖鞭光,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異泞遗,居然都是意外死亡惰许,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)史辙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)汹买,“玉大人,你說(shuō)我怎么就攤上這事聊倔』薇校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵耙蔑,是天一觀(guān)的道長(zhǎng)见妒。 經(jīng)常有香客問(wèn)我,道長(zhǎng)甸陌,這世上最難降的妖魔是什么须揣? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任盐股,我火速辦了婚禮,結(jié)果婚禮上耻卡,老公的妹妹穿的比我還像新娘疯汁。我一直安慰自己,他們只是感情好卵酪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布幌蚊。 她就那樣靜靜地躺著,像睡著了一般溃卡。 火紅的嫁衣襯著肌膚如雪溢豆。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天塑煎,我揣著相機(jī)與錄音沫换,去河邊找鬼。 笑死最铁,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的垮兑。 我是一名探鬼主播冷尉,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼系枪!你這毒婦竟也來(lái)了雀哨?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤私爷,失蹤者是張志新(化名)和其女友劉穎雾棺,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體衬浑,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捌浩,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了工秩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尸饺。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖助币,靈堂內(nèi)的尸體忽然破棺而出浪听,到底是詐尸還是另有隱情,我是刑警寧澤眉菱,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布迹栓,位于F島的核電站,受9級(jí)特大地震影響俭缓,放射性物質(zhì)發(fā)生泄漏克伊。R本人自食惡果不足惜叉抡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望答毫。 院中可真熱鬧褥民,春花似錦、人聲如沸洗搂。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)耘拇。三九已至撵颊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間惫叛,已是汗流浹背倡勇。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘉涌,地道東北人妻熊。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仑最,于是被迫代替她去往敵國(guó)和親扔役。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 第一節(jié)AOP基礎(chǔ)Aspect Oriented Programming (面向切面編程)作用:1)一次解決一類(lèi)問(wèn)...
    RobertLiu123閱讀 338評(píng)論 0 0
  • 1警医、什么是 AOP (1)面向切面編程(方面)亿胸,利用 AOP 可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各...
    鄙人_阿K閱讀 585評(píng)論 0 0
  • 目錄 1. Web MVC發(fā)展史歷程2.Spring概要3.Spring-依賴(lài)注入概要(IOC)4.屬性注入的三種...
    唯老閱讀 1,445評(píng)論 0 3
  • 1. AOP 1.1 AOP介紹 1.1.1 什么是AOP 在軟件業(yè)预皇,AOP為Aspect Oriented Pr...
    itzhouq的筆記閱讀 163評(píng)論 0 3
  • 首先侈玄,為了讓大家能更有效的理解AOP,先帶大家過(guò)一下AOP中的術(shù)語(yǔ): 切面(Aspect):指關(guān)注點(diǎn)模塊化吟温,這個(gè)關(guān)...
    聯(lián)旺閱讀 364評(píng)論 0 0