<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Spring Aop 掃盲

          共 5423字,需瀏覽 11分鐘

           ·

          2020-02-15 23:21


          本文公眾號(hào)來(lái)源:Java建設(shè)者作者:cxuan本文已收錄至我的GitHub



          關(guān)于AOP

          面向切面編程(Aspect-oriented Programming,俗稱AOP)提供了一種面向?qū)ο缶幊?Object-oriented Programming,俗稱OOP)的補(bǔ)充,面向?qū)ο缶幊套詈诵牡膯卧穷?lèi)(class),然而面向切面編程最核心的單元是切面(Aspects)。與面向?qū)ο蟮捻樞蛄鞒滩煌珹OP采用的是橫向切面的方式,注入與主業(yè)務(wù)流程無(wú)關(guān)的功能,例如事務(wù)管理和日志管理。

          94ffe93a3132e9eab551ad482f7842cb.webp

          Spring的一個(gè)關(guān)鍵組件是AOP框架。雖然Spring IoC容器不依賴于AOP(意味著你不需要在IOC中依賴AOP),但AOP為Spring IoC提供了非常強(qiáng)大的中間件解決方案。

          AOP 是一種編程范式,最早由 AOP 聯(lián)盟的組織提出的,通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。它是 OOP的延續(xù)。利用 AOP 可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行隔離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低,提高程序的可重用性,同時(shí)提高了開(kāi)發(fā)的效率

          我們之間的開(kāi)發(fā)流程都是使用順序流程,那么使用 AOP 之后,你就可以橫向抽取重復(fù)代碼,什么叫橫向抽取呢?或許下面這幅圖你能理解,先來(lái)看一下傳統(tǒng)的軟件開(kāi)發(fā)存在什么樣風(fēng)險(xiǎn)。

          縱向繼承體系

          6a2d96ca4ca798a670afcb2eab81885c.webp

          在改進(jìn)方案之前,我們或許都遇到過(guò) IDEA 對(duì)你輸出 Duplicate Code 的時(shí)候,這個(gè)時(shí)候的類(lèi)的設(shè)計(jì)是很糟糕的,代碼寫(xiě)的也很冗余,基本上 if...else... 完成所有事情,這個(gè)時(shí)候就需要把相同的代碼抽取出來(lái)成為公共的方法,降低耦合性。這種提取代碼的方式是縱向抽取,縱向抽取的代碼之間的關(guān)聯(lián)關(guān)系非常密切。

          橫向抽取

          15a1f64291cb7fc2373dcf5638ef3190.webp

          橫向抽取也是代碼提取的一種方式,不過(guò)這種方式不會(huì)修改主要業(yè)務(wù)邏輯代碼,只是在此基礎(chǔ)上添加一些與主要的業(yè)務(wù)邏輯無(wú)關(guān)的功能,AOP 采取橫向抽取機(jī)制,補(bǔ)充了傳統(tǒng)縱向繼承體系(OOP)無(wú)法解決的重復(fù)性 代碼優(yōu)化(性能監(jiān)視、事務(wù)管理、安全檢查、緩存),將業(yè)務(wù)邏輯和系統(tǒng)處理的代碼(關(guān)閉連接、事務(wù)管理、操作日志記錄)解耦。


          AOP 的概念

          在深入學(xué)習(xí)SpringAOP 之前,讓我們先對(duì)AOP的幾個(gè)基本術(shù)語(yǔ)有個(gè)大致的概念,這些概念不是很容易理解,比較抽象,可以知道有這么幾個(gè)概念,下面一起來(lái)看一下:

          • 切面(Aspect):Aspect 聲明類(lèi)似于 Java 中的類(lèi)聲明,事務(wù)管理是AOP一個(gè)最典型的應(yīng)用。在AOP中,切面一般使用 @Aspect 注解來(lái)使用,在XML 中,可以使用 來(lái)定義一個(gè)切面。

          • 連接點(diǎn)(Join Point): 一個(gè)在程序執(zhí)行期間的某一個(gè)操作,就像是執(zhí)行一個(gè)方法或者處理一個(gè)異常。在Spring AOP中,一個(gè)連接點(diǎn)就代表了一個(gè)方法的執(zhí)行。

          • 通知(Advice):在切面中(類(lèi))的某個(gè)連接點(diǎn)(方法出)采取的動(dòng)作,會(huì)有四種不同的通知方式:around(環(huán)繞通知),before(前置通知),after(后置通知), exception(異常通知),return(返回通知)。許多AOP框架(包括Spring)將建議把通知作為為攔截器,并在連接點(diǎn)周?chē)S護(hù)一系列攔截器。

          • 切入點(diǎn)(Pointcut):表示一組連接點(diǎn),通知與切入點(diǎn)表達(dá)式有關(guān),并在切入點(diǎn)匹配的任何連接點(diǎn)處運(yùn)行(例如執(zhí)行具有特定名稱的方法)。由切入點(diǎn)表達(dá)式匹配的連接點(diǎn)的概念是AOP的核心,Spring默認(rèn)使用AspectJ切入點(diǎn)表達(dá)式語(yǔ)言。

          • 介紹(Introduction): introduction可以為原有的對(duì)象增加新的屬性和方法。例如,你可以使用introduction使bean實(shí)現(xiàn)IsModified接口,以簡(jiǎn)化緩存。

          • 目標(biāo)對(duì)象(Target Object): 由一個(gè)或者多個(gè)切面代理的對(duì)象。也被稱為"切面對(duì)象"。由于Spring AOP是使用運(yùn)行時(shí)代理實(shí)現(xiàn)的,因此該對(duì)象始終是代理對(duì)象。

          • AOP代理(AOP proxy): 由AOP框架創(chuàng)建的對(duì)象,在Spring框架中,AOP代理對(duì)象有兩種:JDK動(dòng)態(tài)代理和CGLIB代理

          • 織入(Weaving): 是指把增強(qiáng)應(yīng)用到目標(biāo)對(duì)象來(lái)創(chuàng)建新的代理對(duì)象的過(guò)程,它(例如 AspectJ 編譯器)可以在編譯時(shí)期,加載時(shí)期或者運(yùn)行時(shí)期完成。與其他純Java AOP框架一樣,Spring AOP在運(yùn)行時(shí)進(jìn)行織入。

          Spring AOP 中通知的分類(lèi)

          • 前置通知(Before Advice): 在目標(biāo)方法被調(diào)用前調(diào)用通知功能;相關(guān)的類(lèi)org.springframework.aop.MethodBeforeAdvice

          • 后置通知(After Advice): 在目標(biāo)方法被調(diào)用之后調(diào)用通知功能;相關(guān)的類(lèi)org.springframework.aop.AfterReturningAdvice

          • 返回通知(After-returning): 在目標(biāo)方法成功執(zhí)行之后調(diào)用通知功能;

          • 異常通知(After-throwing): 在目標(biāo)方法拋出異常之后調(diào)用通知功能;相關(guān)的類(lèi)org.springframework.aop.ThrowsAdvice

          • 環(huán)繞通知(Around): 把整個(gè)目標(biāo)方法包裹起來(lái),在被調(diào)用前和調(diào)用之后分別調(diào)用通知功能相關(guān)的類(lèi)org.aopalliance.intercept.MethodInterceptor

          Spring AOP 中織入的三種時(shí)期

          • 編譯期: 切面在目標(biāo)類(lèi)編譯時(shí)被織入,這種方式需要特殊的編譯器。AspectJ 的織入編譯器就是以這種方式織入切面的。

          • 類(lèi)加載期: 切面在目標(biāo)類(lèi)加載到 JVM 時(shí)被織入,這種方式需要特殊的類(lèi)加載器( ClassLoader ),它可以在目標(biāo)類(lèi)引入應(yīng)用之前增強(qiáng)目標(biāo)類(lèi)的字節(jié)碼。

          • 運(yùn)行期: 切面在應(yīng)用運(yùn)行的某個(gè)時(shí)期被織入。一般情況下,在織入切面時(shí),AOP容器會(huì)為目標(biāo)對(duì)象動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象,Spring AOP 采用的就是這種織入方式。

          AOP 的兩種實(shí)現(xiàn)方式

          AOP 采用了兩種實(shí)現(xiàn)方式:靜態(tài)織入(AspectJ 實(shí)現(xiàn))和動(dòng)態(tài)代理(Spring AOP實(shí)現(xiàn))

          AspectJ

          AspectJ 是一個(gè)采用Java 實(shí)現(xiàn)的AOP框架,它能夠?qū)Υa進(jìn)行編譯(一般在編譯期進(jìn)行),讓代碼具有AspectJ 的 AOP 功能,AspectJ 是目前實(shí)現(xiàn) AOP 框架中最成熟,功能最豐富的語(yǔ)言。ApectJ 主要采用的是編譯期靜態(tài)織入的方式。在這個(gè)期間使用 AspectJ 的 acj 編譯器(類(lèi)似 javac)把 aspect 類(lèi)編譯成 class 字節(jié)碼后,在 java 目標(biāo)類(lèi)編譯時(shí)織入,即先編譯 aspect 類(lèi)再編譯目標(biāo)類(lèi)。

          0c2da6f7a251720bdf090ffa4e29ca98.webp

          Spring AOP 實(shí)現(xiàn)

          Spring AOP 是通過(guò)動(dòng)態(tài)代理技術(shù)實(shí)現(xiàn)的,而動(dòng)態(tài)代理是基于反射設(shè)計(jì)的。Spring AOP 采用了兩種混合的實(shí)現(xiàn)方式:JDK 動(dòng)態(tài)代理和 CGLib 動(dòng)態(tài)代理,分別來(lái)理解一下

          a71745d2ee21f1c2d3b7e58ad025b2e4.webp

          • JDK動(dòng)態(tài)代理:Spring AOP的首選方法。每當(dāng)目標(biāo)對(duì)象實(shí)現(xiàn)一個(gè)接口時(shí),就會(huì)使用JDK動(dòng)態(tài)代理。目標(biāo)對(duì)象必須實(shí)現(xiàn)接口

          • CGLIB代理:如果目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,則可以使用CGLIB代理。

          Spring 對(duì) AOP的支持

          Spring 提供了兩種AOP 的實(shí)現(xiàn):基于注解式配置和基于XML配置

          @AspectJ 支持

          為了在Spring 配置中使用@AspectJ ,你需要啟用Spring支持,以根據(jù)@AspectJ切面配置Spring AOP,并配置自動(dòng)代理。自動(dòng)代理意味著,Spring 會(huì)根據(jù)自動(dòng)代理為 Bean 生成代理來(lái)攔截方法的調(diào)用,并確保根據(jù)需要執(zhí)行攔截。

          可以使用XML或Java樣式配置啟用@AspectJ支持。在任何一種情況下,都還需要確保AspectJ的aspectjweaver.jar 第三方庫(kù)位于應(yīng)用程序的類(lèi)路徑中(版本1.8或更高版本)。

          開(kāi)啟@AspectJ 支持

          使用@Configuration 支持@AspectJ 的時(shí)候,需要添加 @EnableAspectJAutoProxy 注解,就像下面例子展示的這樣來(lái)開(kāi)啟 AOP代理

          @Configuration
          @EnableAspectJAutoProxy
          public class AppConfig {}

          也可以使用XML配置來(lái)開(kāi)啟@AspectJ 支持

          <aop:aspectj-autoproxy/>

          默認(rèn)你已經(jīng)添加了 aop 的schema 空間,如果沒(méi)有的話,你需要手動(dòng)添加


          <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 https://www.springframework.org/schema/beans/spring-beans.xsd
          http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd"
          >



          beans>

          聲明一個(gè)切面

          在啟用了@AspectJ支持的情況下,在應(yīng)用程序上下文中定義的任何bean都具有@AspectJ方面的類(lèi)(具有@Aspect注釋?zhuān)琒pring會(huì)自動(dòng)檢測(cè)并用于配置Spring AOP。

          使用XML 配置的方式定義一個(gè)切面

          <aop:aspect />

          使用注解的方式定義一個(gè)切面

          @Aspect
          public class MyAspect {}

          切面(也就是用@Aspect注解的類(lèi))就像其他類(lèi)一樣有屬性和方法。它們能夠包含切入點(diǎn),通知和介紹聲明。

          通過(guò)自動(dòng)掃描檢測(cè)切面

          你可以在Spring XML 配置中將切面類(lèi)注冊(cè)為常規(guī)的bean,或者通過(guò)類(lèi)路徑掃描自動(dòng)檢測(cè)它們 - 與任何其他Spring管理的bean相同。然而,只是注解了@Aspect 的類(lèi)不會(huì)被當(dāng)作bean 進(jìn)行管理,你還需要在類(lèi)上面添加 @Component 注解,把它當(dāng)作一個(gè)組件交給 Spring 管理。

          定義一個(gè)切點(diǎn)

          一個(gè)切點(diǎn)由兩部分組成:包含名稱和任何參數(shù)以及切入點(diǎn)表達(dá)式的簽名,該表達(dá)式能夠確定我們想要執(zhí)行的方法。在@AspectJ注釋風(fēng)格的AOP中,切入點(diǎn)表達(dá)式需要用@Pointcut注解標(biāo)注(這個(gè)表達(dá)式作為方法的簽名,它的返回值必須是 void)。

          @Pointcut("execution(* transfer(..))") // 切入點(diǎn)表達(dá)式
          private void definePointcut() {}// 方法簽名

          切入點(diǎn)表達(dá)式的編寫(xiě)規(guī)則如下:

          b8d1518e36cb913573ba56537a5c0996.webp

          現(xiàn)在假設(shè)我們需要配置的切點(diǎn)僅僅匹配指定的包,就可以使用 within() 限定符來(lái)表示,如下表達(dá)式所述:

          f5313c00deb76b2c5b52532c2d433c5e.webp

          請(qǐng)注意我們使用了 && 操作符把 execution() 和 within() 指示器連接在一起,表示的是 的關(guān)系,類(lèi)似的,你還可以使用 || 操作來(lái)表示 的關(guān)系, 使用 ! 表示 的關(guān)系。

          除了within() 表示的限定符外,還有其它的限定符,下面是一個(gè)限定符表

          AspectJ 描述符描述
          arg()限制連接點(diǎn)匹配參數(shù)為指定類(lèi)型的執(zhí)行方法
          @args()限制連接點(diǎn)匹配參數(shù)由指定注解標(biāo)注的執(zhí)行方法
          execution()用于匹配是連接點(diǎn)的執(zhí)行方法
          this()限制連接點(diǎn)匹配的AOP代理的bean引用為指定類(lèi)型的類(lèi)
          target限制連接點(diǎn)匹配目標(biāo)對(duì)象為指定類(lèi)型的類(lèi)
          @target()限制連接點(diǎn)匹配特定的執(zhí)行對(duì)象,這些對(duì)象對(duì)應(yīng)的類(lèi)要具有指定類(lèi)型的注解
          within()限制連接點(diǎn)匹配指定的類(lèi)型
          @within()限制連接點(diǎn)匹配指定注解所標(biāo)注的類(lèi)型
          @annotationn限定匹配帶有指定注解的連接點(diǎn)

          使用XML配置來(lái)配置切點(diǎn)

          <aop:config>
          <aop:aspect ref = "">
          <aop:poincut id = "" expression="execution(** com.cxuan.aop.definePointcut(......))"/>
          aop:aspect>
          aop:config>

          聲明一個(gè)通知

          通知是和切入點(diǎn)表達(dá)式相互關(guān)聯(lián),用于在方法執(zhí)行之前,之后或者方法前后,方法返回,方法拋出異常時(shí)調(diào)用通知的方法,切入點(diǎn)表達(dá)式可以是對(duì)命名切入點(diǎn)的簡(jiǎn)單引用,也可以是在適當(dāng)位置聲明的切入點(diǎn)表達(dá)式。下面以一個(gè)例子來(lái)演示一下這些通知都是如何定義的:

          上面的例子就很清晰了,定義了一個(gè) Audience 切面,并在切面中定義了一個(gè)performance() 的切點(diǎn),下面各自定義了表演之前、表演之后返回、表演失敗的時(shí)候進(jìn)行通知,除此之外,你還需要在main 方法中開(kāi)啟 @EnableAspectJAutoProxy 來(lái)開(kāi)啟自動(dòng)代理。

          除了使用Java Config 的方式外,你還可以使用基于XML的配置方式

          6775b5a250600237fd3b9a9a8cf88a77.webp

          當(dāng)然,這種切點(diǎn)定義的比較冗余,為了解決這種類(lèi)似 if...else... 災(zāi)難性的業(yè)務(wù)邏輯,你需要單獨(dú)定義一個(gè),然后使用 pointcut-ref 屬性指向上面那個(gè)標(biāo)簽,就像下面這樣

          d1d88a318c6d7dbd35c81dcc89e04893.webp

          環(huán)繞通知

          在目標(biāo)方法執(zhí)行之前和之后都可以執(zhí)行額外代碼的通知。在環(huán)繞通知中必須顯式的調(diào)用目標(biāo)方法,目標(biāo)方法才會(huì)執(zhí)行,這個(gè)顯式調(diào)用時(shí)通過(guò)ProceedingJoinPoint來(lái)實(shí)現(xiàn)的,可以在環(huán)繞通知中接收一個(gè)此類(lèi)型的形參,spring容器會(huì)自動(dòng)將該對(duì)象傳入,注意這個(gè)參數(shù)必須處在環(huán)繞通知的第一個(gè)形參位置。

          環(huán)繞通知需要返回返回值,否則真正調(diào)用者將拿不到返回值,只能得到一個(gè)null。下面是環(huán)繞通知的一個(gè)示例

           <aop:around method="around" pointcut-ref="pc1"/>
           public Object around(ProceedingJoinPoint jp) throws Throwable{
          System.out.println("1 -- around before...");
          Object obj = jp.proceed(); //--顯式的調(diào)用目標(biāo)方法
          System.out.println("1 -- around after...");
          return obj;
          }

          戳:百萬(wàn)字長(zhǎng)文帶你學(xué)習(xí)「Java」


          如果大家想要實(shí)時(shí)關(guān)注我更新的文章以及分享的干貨的話,可以關(guān)注我的公眾號(hào)Java3y

          • 獲取Java精美腦圖a3be99f7c65d063c231918ae1f260dfb.webp

          • ?獲取Java學(xué)習(xí)路線a3be99f7c65d063c231918ae1f260dfb.webp

          • 獲取開(kāi)發(fā)常用工具a3be99f7c65d063c231918ae1f260dfb.webp

          • ?加入技術(shù)交流群a3be99f7c65d063c231918ae1f260dfb.webp

          在公眾號(hào)下回復(fù)「888」即可獲取!!

          b8168bf2453695768d655d4aa206609a.webp

          點(diǎn)個(gè)在看7c2e537d992790bb0d039d0d9e3ebb54.webp,分享到朋友圈85404a15cbfee2144c6493b36698a2b9.webp,對(duì)我真的很重要!!

          瀏覽 50
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  超碰97资源 | 成人午夜精品无码区久久app | cao视频 | 国产精品一级淫荡精品录像 | 韩国TS『人妖av |