當(dāng)前位置:
首頁 >
前端技术
> javascript
>内容正文
javascript
spring(4)面向切面的Spring(AOP)
生活随笔
收集整理的這篇文章主要介紹了
spring(4)面向切面的Spring(AOP)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【0】README
1)本文部分文字描述轉(zhuǎn)自:“Spring In Action(中/英文版)”,旨在review ?“spring(4)面向切面的Spring(AOP)”?的相關(guān)知識(shí);
2)在軟件開發(fā)中,散布于應(yīng)用中多處的功能被稱為橫切關(guān)注點(diǎn)。通常來講,這些橫切關(guān)注點(diǎn)從概念上是與應(yīng)用的業(yè)務(wù)邏輯相分離的(但是往往會(huì)直接嵌入到應(yīng)用的業(yè)務(wù)邏輯中)。把這些橫切關(guān)注點(diǎn)與業(yè)務(wù)邏輯相分離正是面向切面編程(AOP)所要解決的問題;(干貨——引入橫切關(guān)注點(diǎn)及其所要解決的問題)
3)要知道,DI有助于應(yīng)用對(duì)象之間的解耦,而AOP 可以實(shí)現(xiàn)橫切關(guān)注點(diǎn)與它們所影響的對(duì)象之間的解耦;(干貨——DI 和 AOP 的作用)
4)本文內(nèi)容概覽:
4.1)暫時(shí)了Spring對(duì)切面的支持,包括如何把普通類聲明為一個(gè)切面和如何使用注解創(chuàng)建切面; 4.2)除此之外,還可以看到 AspectJ——另一種流行的AOP實(shí)現(xiàn)——如何補(bǔ)充Spring AOP的功能;(干貨——引入AspectJ框架)
【1】什么是面向切面編程 1)problem+solution: 1.1)problem:如果要重用通用功能的話,最常見的技術(shù)是繼承或委托。但是,如果在整個(gè)應(yīng)用中都使用相同的基類,繼承往往會(huì)導(dǎo)致一個(gè)脆弱的對(duì)象體系; 1.2)solution:切面提供了取代繼承和委托的另一種可選方案; 2)切面:橫切關(guān)注點(diǎn)可以被模塊化為特殊的類,這些類被稱為切面;這樣做有兩個(gè)好處(merits)(干貨——切面的定義) m1)現(xiàn)在每個(gè)關(guān)注點(diǎn)都集中于一個(gè)地方,而不是分散到多處; m2)服務(wù)模塊更加簡(jiǎn)潔了,因?yàn)樗鼈冎话饕P(guān)注點(diǎn)的代碼,而次要關(guān)注點(diǎn)的代碼被轉(zhuǎn)移到切面中了;
【1.1】定義AOP術(shù)語 1)描述切面的術(shù)語有:通知,切點(diǎn),連接點(diǎn); 1.1)通知(Advice):切面的工作被稱為通知;通知定義了切面是什么以及何時(shí)使用;(5中類型的通知:前置通知,后置通知,返回通知,異常通知,環(huán)繞通知)
1.2)連接點(diǎn)(Join point):是在應(yīng)用執(zhí)行過程中能夠插入切面的一個(gè)點(diǎn); 1.3)切點(diǎn)(Pointcut):如果說通知定義了切面是什么和何時(shí)執(zhí)行的話,那么切點(diǎn)就定義了 何處(在何處執(zhí)行),切點(diǎn)的定義會(huì)匹配通知所要織入的一個(gè)或多個(gè)連接點(diǎn); 1.4)切面(Aspect):切面是通知和切點(diǎn)的結(jié)合。通知和切點(diǎn)共同定義了切面的全部?jī)?nèi)容——它是什么? 1.5)引入(Introduction):引入允許我們向現(xiàn)有的類添加新方法或?qū)傩?#xff1b; 1.6)織入(Weaving):織入是吧切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過程;切面在指定的連接點(diǎn)被織入到目標(biāo)對(duì)象中。在目標(biāo)對(duì)象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入: peroid1)編譯期:切面在目標(biāo)類編譯時(shí)被織入;AspectJ的織入編譯器就是以這種方式織入切面的; peroid2)類加載期:切面在目標(biāo)類加載到 jvm 時(shí) 被織入;這種方式需要特殊的類加載器(ClassLoader),它可以在目標(biāo)類被引入應(yīng)用之前增強(qiáng)該目標(biāo)類的字節(jié)碼;AspectJ 5 的加載時(shí)織入(load-time weaving,LTW)就支持以這種方式織入切面; period3)運(yùn)行期:切面在應(yīng)用運(yùn)行的某個(gè)時(shí)刻被織入;一般case下,在織入切面時(shí),AOP 容器會(huì)為目標(biāo)對(duì)象動(dòng)態(tài)地創(chuàng)建一個(gè)代理對(duì)象。Spring AOP 就是以這種方式織入切面的;
【1.2】Spring對(duì)AOP 的支持 1)Spring 和 AspectJ 之間有大量的協(xié)作,而且Spring 對(duì) AOP 的支持在很多方面借鑒了 AspectJ 項(xiàng)目; 2)spring 提供了4中類型的 AOP支持: type1)基于代理的經(jīng)典 spring AOP; type2)純 POJO 切面; type3)@AspectJ 注解驅(qū)動(dòng)的切面; type4)注入式 AspectJ 切面(適用于spring 各種version); Attention) A1)前三種都是 spring aop 實(shí)現(xiàn)的變體,spring aop 構(gòu)建在動(dòng)態(tài)代理基礎(chǔ)之上:因此,spring 對(duì) aop的支持局限于方法攔截;(干貨——spring 對(duì) aop的支持局限于方法攔截) A2)spring 借鑒了AspectJ 的切面,以提供注解驅(qū)動(dòng)的AOP。本質(zhì)上,它依然是 spring 基于代理的aop,但是編程模型幾乎與編寫成熟的 AspectJ 注解切面完全一致。這種aop 風(fēng)格的好處在于能夠不使用XML 來完成功能; 3)spring 在運(yùn)行時(shí)通知對(duì)象:通過在代理類中包裹切面,spring 在運(yùn)行期吧切面織入到 spring管理的bean中。 如下圖所示。代理類封裝了目標(biāo)類,并攔截被通知方法的調(diào)用,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標(biāo)bean。當(dāng)代理攔截到方法調(diào)用時(shí),在調(diào)用目標(biāo)bean 方法之前,會(huì)執(zhí)行切面邏輯;
4)spring 只支持方法級(jí)別的連接點(diǎn): 通過使用各種aop 方案可以支持多種連接點(diǎn)模型; 4.1)因?yàn)閟pring基于動(dòng)態(tài)代理,所以spring 只支持方法連接點(diǎn)。這與一些其他的AOP 框架是不同的,例如 AspectJ 和 JBoss,除了方法切點(diǎn)外,它們還提供了字段和構(gòu)造器接入點(diǎn);(干貨——因?yàn)閟pring基于動(dòng)態(tài)代理,所以spring 只支持方法連接點(diǎn)) 4.2)方法攔截可以滿足絕大部分的需求;如果需要方法攔截之外的連接點(diǎn)攔截功能,可以利用 Aspect 來補(bǔ)充 spring aop 的功能;
【2】通過切點(diǎn)來選擇連接點(diǎn) 1)關(guān)于spring aop 的AspectJ 切點(diǎn),最重要的一點(diǎn)是:spring僅僅支持AspectJ 切點(diǎn)指示器的一個(gè)子集; 2)review:spring是基于代理的,而某些切點(diǎn)表達(dá)式是與基于代理的aop 無關(guān)的,下表列出了 spring aop 所支持的 AspectJ 切點(diǎn)指示器;
Attention)只有 execution指示器: 是實(shí)際執(zhí)行匹配的,而其他的指示器都是用來限制匹配的;它是最主要使用的指示器;
【2.1】編寫切點(diǎn) 1)需要有個(gè)主題來定義切面的切點(diǎn): public interface Performance {public void perform(); }2)表達(dá)式execution(* concert.Performance.perform(..)):這個(gè)表達(dá)式能夠設(shè)置當(dāng) perform()方法執(zhí)行時(shí)觸發(fā)通知的調(diào)用;
3)假設(shè)我們需要匹配的切點(diǎn)僅匹配 concert 包,在此case下,可以使用 ?within() 指示器來限制匹配, 如下圖所示;
【2.2】在切點(diǎn)中選擇bean 1)bean()指示器:spring 引入了新的bean()指示器,它允許我們?cè)谇悬c(diǎn)表達(dá)式中使用 bean ?的ID 來標(biāo)識(shí) bean。(干貨——引入指示器的概念) 2)看個(gè)荔枝: execution(* concert.Performance.perform()) ?and bean('jaychou') :我們希望在執(zhí)行Performance.perform方法時(shí)應(yīng)用通知,但限定bean的ID為?jaychou; execution(* concert.Performance.perform()) ?and !bean('jaychou'):?我們希望在執(zhí)行Performance.perform方法時(shí)應(yīng)用通知,但限定bean的ID不為?jaychou;
【3】使用注解創(chuàng)建切面 1)intro:使用注解來創(chuàng)建切面是 AspectJ 5 所引入 的關(guān)鍵特性;
【3.1】定義切面 1)看個(gè)荔枝:將考生定義為一個(gè)切面; @Aspect public class Examinee { // 考生@Before("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void checkIdentity() { // 身份審查System.out.println("please show us your admission ticket.");}@Before("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void checkSecurity() { // 安檢System.out.println("please accept electronic equipment inspection.");}@AfterReturning("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void handinPaper() { // 交卷System.out.println("please hand in an examination paper.");}@AfterThrowing("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void cancelQualification() { // 取消考試資格System.out.println("cancel the exam qualification");} } 對(duì)以上代碼的分析(Analysis): A1)spring使用了 AspectJ 注解來聲明通知方法, 如下表所示:
A2)上面的注解都給定了一個(gè)切點(diǎn)表達(dá)式作為它的值,且這4個(gè)切點(diǎn)表達(dá)式都是相同的,其實(shí)它們可以設(shè)置成為不同的切點(diǎn)表達(dá)式;
3)problem+solution: 3.1)problem:相同的切點(diǎn)表達(dá)式出現(xiàn)了四遍,這可是不光彩的事情; 3.2)solution:引入了 @PointCut注解, 該注解能夠在一個(gè) @AspectJ 切面內(nèi)定義可重用個(gè)切點(diǎn); @Aspect public class ExamineeVersion2 {@Pointcut("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void chineseExamination(){}@Before("chineseExamination()")public void checkIdentity() { // 身份審查System.out.println("please show us your admission ticket.");}@Before("chineseExamination()")public void checkSecurity() { // 安檢System.out.println("please accept electronic equipment inspection.");}@AfterReturning("chineseExamination()")public void handinPaper() { // 交卷System.out.println("please hand in an examination paper.");}@AfterThrowing("chineseExamination()")public void cancelQualification() { // 取消考試資格System.out.println("cancel the exam qualification");} } 對(duì)以上代碼的分析(Analysis): A1)在 Examinee中,chineseExamination()方法使用了 @Pointcut 注解,為@Pointcut注解設(shè)置的值是一個(gè)切點(diǎn)表達(dá)式; A2)實(shí)際上擴(kuò)展了切點(diǎn)表達(dá)式語言,這就可以在 任何的切點(diǎn)表達(dá)式中使用?chineseExamination()方法了;
4)problem+solution: 4.1)problem:如果僅僅是以上代碼的話,即便使用了AspectJ 注解,但它也不會(huì)被視為切面,這些注解不會(huì)解析,也不會(huì)創(chuàng)建將其轉(zhuǎn)換為切面的代理; 4.2)solution:如果使用JavaConfig的話,可以在配置類的類級(jí)別上通過使用 @EnableAspectJAutoProxy 注解啟用自動(dòng)代理功能;(干貨——@EnableAspectJAutoProxy 注解的作用) @Configuration @EnableAspectJAutoProxy @ComponentScan public class ExaminationConfig {@Beanpublic Examinee getExaminee() {return new Examinee("xiaotang");} } 4.3)如果使用XML配置類裝配bean的話,使用 spring aop命名空間中的 <aop:aspectj-autoproxy> 元素:
Attention)? A1)不管使用JavaConfig 還是 XML配置來裝配:AspectJ 自動(dòng)代理都會(huì)為使用 @AspectJ 注解的bean創(chuàng)建一個(gè)代理,這個(gè)代理會(huì)圍繞著所有該切面的切點(diǎn)所匹配的bean; A2)在這種case下,將會(huì)為 Examination bean創(chuàng)建一個(gè)代理,Examinee類中的通知方法將會(huì)在 chineseExamination() 方法調(diào)用前后執(zhí)行; A3)spring的 AspectJ 自動(dòng)代理僅僅是使用 @AspectJ 作為創(chuàng)建切面的指導(dǎo),切面依然是基于代理的;本質(zhì)上,它依然是 spring基于代理的切面;
【3.2】創(chuàng)建環(huán)繞通知 1)intro:環(huán)繞通知是最強(qiáng)大的通知類型。它能夠讓你所編寫的邏輯將被通知的目標(biāo)方法完全包裝起來。實(shí)際上就想是在一個(gè)通知方法中通知編寫前置通知和后置通知; 2)看個(gè)荔枝:重寫 Eaxminee 切面; // 考生(就是一個(gè)切面) @Aspect public class ExamineeVersion3 {@Pointcut("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void chineseExamination() {}@Around("chineseExamination()")public void doChineseExamination(ProceedingJoinPoint jp) {try {System.out.println("please show us your admission ticket.");System.out.println("please accept electronic equipment inspection.");jp.proceed();System.out.println("please hand in an examination paper.");} catch (Throwable e) {System.out.println("cancel the exam qualification");}} } 對(duì)以上代碼的分析(Analysis): A1)@Around注解表明?doChineseExamination() 方法會(huì)作為?chineseExamination() 切點(diǎn)的環(huán)繞通知;(干貨——@Around注解的作用) A2)別忘記調(diào)用 ProceedingJoinPoint.proceed()方法,如果不調(diào)用這個(gè)方法的話,那么你的通知實(shí)際上會(huì)阻塞對(duì)被通知方法的調(diào)用; A3)當(dāng)然,你可以不調(diào)用ProceedingJoinPoint.proceed()方法,你也可以在通知中對(duì)它進(jìn)行多次調(diào)用;
【3.3】處理通知中的參數(shù) 1)除了環(huán)繞通知,我們編寫的其他通知不需要關(guān)注傳遞給被通知方法的任意參數(shù); 2)如果切面所通知的方法確實(shí)有參數(shù)該怎么辦?切面能訪問和使用傳遞給被通知方法的參數(shù)嗎? 3)看個(gè)荔枝:
對(duì)以上代碼的分析(Analysis): A1)這里的不同點(diǎn)在于切點(diǎn)還聲明了要提供給通知方法的參數(shù);
A2)關(guān)注 args(trackNumber))限定符:它表明傳遞給 playTrack()方法的int類型參數(shù)也會(huì)傳遞到通知里面去,參數(shù)的名稱 trackNumber 也與切點(diǎn)方法簽名中的參數(shù)相匹配; A3)這個(gè)參數(shù)會(huì)傳遞到通知方法中:該方法是通過 @Before 注解和命名切點(diǎn) trackPlayed(trackNumber)定義的。切點(diǎn)定義中的參數(shù)與切點(diǎn)方法中的參數(shù)名稱是一樣的,這樣就完成了從命名切點(diǎn)到通知方法的參數(shù)轉(zhuǎn)移;
@Aspect public class ExamineeArgs {private String name;public ExamineeArgs(String name) {this.name = name;}@Pointcut("execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int)) "+ "&& args(examSiteCode)")public void myGetExamineeNumber(int examSiteCode) {}@Before("myGetExamineeNumber(examSiteCode)")public void checkIdentity(int examSiteCode) { // 身份審查System.out.println("@Before: your exam site code is " + examSiteCode);} } 【3.4】通過注解引入新功能 1)intro:切面能夠?yàn)楝F(xiàn)有的方法增加新功能,利用aop概念,切面與可以為 spring bean 添加新方法;
2)當(dāng)引入接口的方法被調(diào)用時(shí),代理會(huì)把此調(diào)用委托給實(shí)現(xiàn)了新接口的某個(gè)其他對(duì)象。實(shí)際上,一個(gè)bean的實(shí)現(xiàn)被拆分到了多個(gè)類中;(干貨——一個(gè)spring bean的實(shí)現(xiàn)被拆分到了多個(gè)類中) 3)借助于aop 的引入功能,可以不必在設(shè)計(jì)上妥協(xié)或侵入性地改變現(xiàn)有的實(shí)現(xiàn)。為了實(shí)現(xiàn)該功能,我們要?jiǎng)?chuàng)建一個(gè)新切面; @Aspect public class ExamineeArgs {private String name;public ExamineeArgs(String name) {this.name = name;}@Pointcut("execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int)) "+ "&& args(examSiteCode)")public void myGetExamineeNumber(int examSiteCode) { }@Before("myGetExamineeNumber(examSiteCode)")public void checkIdentity(int examSiteCode) { // 身份審查System.out.println("@Before: your exam site code is " + examSiteCode);} }// 高考 public class CollegeEntraceExamination {public CollegeEntraceExamination() { }public void chineseExamination() {System.out.println("chinese examination is underway.");}public int getExamineeNumber(int examSiteCode) {return 1000;} } //---------------------------------------我是分割線. @Aspect public class AdditionExaminationIntroducer{@DeclareParents(value="com.spring.chapter4.CollegeEntraceExamination.chineseExamination+",defaultImpl=AdditionExaminationImpl.class)public static AddtionExamination addtionExamination; } public interface AddtionExamination {void doAddtionalExamination(); } public class AdditionExaminationImpl implements AddtionExamination{@Overridepublic void doAddtionalExamination() {System.out.println("do additional questions as possible as you can.");} } 對(duì)以上代碼的分析(Analysis):
A1)這個(gè)切面與以往切面不同的是:它并沒有前置,后置等通知,而是通過 @DeclareParents注解將?AddtionExamination? 接口引入到?CollegeEntraceExamination bean中; A2)@DeclareParents注解由3個(gè)部分組成:(干貨——@DeclareParents注解的作用) part1)value屬性:指定了哪種類型的bean 要引入該接口; part2)defaultImpl:指定了為引入功能提供實(shí)現(xiàn)的類; part3)@DeclareParents注解所標(biāo)注的靜態(tài)屬性:指明了要引入的接口; A3)和其它切面一樣,需要在spring 應(yīng)用中將 AdditionExaminationIntroducer 聲明為 一個(gè) bean; <bean class="com.spring.chapter4.AdditionExaminationIntroducer" /> spring 的自動(dòng)代理機(jī)制將會(huì)獲取到它的聲明,但 spring 發(fā)現(xiàn)一個(gè)bean使用 @Aspect注解時(shí),spring就會(huì)創(chuàng)建一個(gè)代理,然后將調(diào)用委托給被代理bean 或被引入的實(shí)現(xiàn),這取決于調(diào)用的方法屬于被代理的bean 還是屬于被引入的接口;(干貨——這解釋了為什么一個(gè)spring bean的實(shí)現(xiàn)被拆分到了多個(gè)類中的問題)
對(duì)上圖的分析(Analysis):我們看到,即使CollegeEntraceExamination 并沒有實(shí)現(xiàn)?AddtionExamination接口,但是通過創(chuàng)建新切面為前者添加了新方法,CollegeEntraceExamination?就可以調(diào)用了doAddtionalExamination()方法了;
4)面向注解的切面聲明有一個(gè)明顯的劣勢(shì):你必須能夠?yàn)橥ㄖ愄砑幼⒔狻榱俗龅竭@一點(diǎn),必須要有源碼;如果沒有源碼,或不想將 AspectJ 注解放到你的代碼中,可以使用 spring XML 配置文件中聲明切面;(干貨——面向注解的切面聲明有一個(gè)明顯的劣勢(shì))
【4】在XML 中聲明切面 1)之前有這樣一個(gè)rule:基于注解的bean裝配要優(yōu)于基于JavaConfig 的裝配,基于JavaConfig的裝配 要優(yōu)于基于XML的配置裝配; 2)但是,如果需要聲明切面,但又不能為通知類添加注解的時(shí)候,那么就必須轉(zhuǎn)向XML 配置了; 3)spring 的aop 配置元素能夠以非侵入性的方式聲明切面
【4.1】聲明前置和后置通知 1)通過XML 將無注解的 Examinee 聲明為切面 <aop:config><aop:aspect ref="examinee"><aop:beforepointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="checkIdentity"/><aop:beforepointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="checkIdentity"/><aop:after-returningpointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="handinPaper"/><aop:after-throwingpointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="cancelQualification"/></aop:aspect> </aop:config> 2)使用<aop:pointcut> 定義命名切點(diǎn) <aop:config><aop:aspect ref="examinee"><aop:pointcutid="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:beforepointcut-ref="chineseExamination"method="checkIdentity"/><aop:beforepointcut-ref="chineseExamination"method="checkIdentity"/><aop:after-returningpointcut-ref="chineseExamination"method="handinPaper"/><aop:after-throwingpointcut-ref="chineseExamination"method="cancelQualification"/></aop:aspect> </aop:config> 圖片t18
Attention)spring4-config.xml 源代碼如下: <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXML"><aop:pointcut id="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:before pointcut-ref="chineseExamination" method="checkIdentity" /><aop:before pointcut-ref="chineseExamination" method="checkSecurity" /><aop:after-returning pointcut-ref="chineseExamination" method="handinPaper" /><aop:after-throwing pointcut-ref="chineseExamination" method="cancelQualification" /></aop:aspect></aop:config> </beans> 【4.2】在XML配置元素中聲明環(huán)繞通知 step1)attendChineseExamination()方法提供了aop環(huán)繞通知; // 考生(就是一個(gè)切面) public class ExamineeAroundXML {private String name;public ExamineeAroundXML(String name) {this.name = name;}public void attendChineseExamination(ProceedingJoinPoint jp) {try {System.out.println("AroundXML@Before: please show us your admission ticket.");System.out.println("AroundXML@Before: please accept electronic equipment inspection.");jp.proceed();System.out.println("AroundXML@AfterReturn: please hand in an examination paper.");} catch (Throwable e) {System.out.println("AroundXML@AfterThrowing: cancel the exam qualification");}} } step2)在XML 中使用 <aop:around>元素聲明環(huán)繞通知 <aop:config><aop:aspect ref="audience"><aop:pointcutid="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:aroundpointcut-ref="chineseExamination"method="attendChineseExamination"/></aop:aspect> </aop:config> 圖片t19
【4.3】 為通知傳遞參數(shù)(在XML中將ExamineeXMLArgs 配置為參數(shù)化的切面 ) 圖片t20
@Configuration @ImportResource("classpath:com/spring/chapter4/spring4-args-config.xml") public class ExaminationConfig {@Beanpublic CollegeEntraceExamination getExamination() {return new CollegeEntraceExamination();}@Bean(name="examineeXMLArgs")public ExamineeXMLArgs getExamineeXMLArgs() {return new ExamineeXMLArgs("xiaotang");} } spring4-args-config.xml 的源碼如下:? <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXMLArgs"><aop:pointcut id="getExamineeNumber"expression="execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int))and args(examSiteCode)" /><aop:before pointcut-ref="getExamineeNumber" method="checkIdentity"/></aop:aspect></aop:config> </beans>
【4.4】通過XML為切面引入新功能 圖片(t21)
@Configuration @ImportResource("classpath:com/spring/chapter4/spring4-args-intro-config.xml") public class ExaminationConfig {@Beanpublic CollegeEntraceExamination getExamination() {return new CollegeEntraceExamination();}@Bean(name="examineeXMLArgs")public ExamineeXMLArgs getExamineeXMLArgs() {return new ExamineeXMLArgs("xiaotang");} } spring4-args-intro-config.xml 的源代碼如下: <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXMLArgs"><aop:pointcut id="getExamineeNumber"expression="execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int))and args(examSiteCode)" /><aop:before pointcut-ref="getExamineeNumber" method="checkIdentity"/></aop:aspect><aop:aspect><aop:declare-parents types-matching="com.spring.chapter4.CollegeEntraceExamination+" implement-interface="com.spring.chapter4.AddtionExamination" default-impl="com.spring.chapter4.AdditionExaminationImpl"<!-- delegate-ref="additionExaminationImpl" 前提是spring容器中有additionExaminationImpl bean--> /></aop:aspect></aop:config> </beans>
【1】什么是面向切面編程 1)problem+solution: 1.1)problem:如果要重用通用功能的話,最常見的技術(shù)是繼承或委托。但是,如果在整個(gè)應(yīng)用中都使用相同的基類,繼承往往會(huì)導(dǎo)致一個(gè)脆弱的對(duì)象體系; 1.2)solution:切面提供了取代繼承和委托的另一種可選方案; 2)切面:橫切關(guān)注點(diǎn)可以被模塊化為特殊的類,這些類被稱為切面;這樣做有兩個(gè)好處(merits)(干貨——切面的定義) m1)現(xiàn)在每個(gè)關(guān)注點(diǎn)都集中于一個(gè)地方,而不是分散到多處; m2)服務(wù)模塊更加簡(jiǎn)潔了,因?yàn)樗鼈冎话饕P(guān)注點(diǎn)的代碼,而次要關(guān)注點(diǎn)的代碼被轉(zhuǎn)移到切面中了;
【1.1】定義AOP術(shù)語 1)描述切面的術(shù)語有:通知,切點(diǎn),連接點(diǎn); 1.1)通知(Advice):切面的工作被稱為通知;通知定義了切面是什么以及何時(shí)使用;(5中類型的通知:前置通知,后置通知,返回通知,異常通知,環(huán)繞通知)
1.2)連接點(diǎn)(Join point):是在應(yīng)用執(zhí)行過程中能夠插入切面的一個(gè)點(diǎn); 1.3)切點(diǎn)(Pointcut):如果說通知定義了切面是什么和何時(shí)執(zhí)行的話,那么切點(diǎn)就定義了 何處(在何處執(zhí)行),切點(diǎn)的定義會(huì)匹配通知所要織入的一個(gè)或多個(gè)連接點(diǎn); 1.4)切面(Aspect):切面是通知和切點(diǎn)的結(jié)合。通知和切點(diǎn)共同定義了切面的全部?jī)?nèi)容——它是什么? 1.5)引入(Introduction):引入允許我們向現(xiàn)有的類添加新方法或?qū)傩?#xff1b; 1.6)織入(Weaving):織入是吧切面應(yīng)用到目標(biāo)對(duì)象并創(chuàng)建新的代理對(duì)象的過程;切面在指定的連接點(diǎn)被織入到目標(biāo)對(duì)象中。在目標(biāo)對(duì)象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入: peroid1)編譯期:切面在目標(biāo)類編譯時(shí)被織入;AspectJ的織入編譯器就是以這種方式織入切面的; peroid2)類加載期:切面在目標(biāo)類加載到 jvm 時(shí) 被織入;這種方式需要特殊的類加載器(ClassLoader),它可以在目標(biāo)類被引入應(yīng)用之前增強(qiáng)該目標(biāo)類的字節(jié)碼;AspectJ 5 的加載時(shí)織入(load-time weaving,LTW)就支持以這種方式織入切面; period3)運(yùn)行期:切面在應(yīng)用運(yùn)行的某個(gè)時(shí)刻被織入;一般case下,在織入切面時(shí),AOP 容器會(huì)為目標(biāo)對(duì)象動(dòng)態(tài)地創(chuàng)建一個(gè)代理對(duì)象。Spring AOP 就是以這種方式織入切面的;
【1.2】Spring對(duì)AOP 的支持 1)Spring 和 AspectJ 之間有大量的協(xié)作,而且Spring 對(duì) AOP 的支持在很多方面借鑒了 AspectJ 項(xiàng)目; 2)spring 提供了4中類型的 AOP支持: type1)基于代理的經(jīng)典 spring AOP; type2)純 POJO 切面; type3)@AspectJ 注解驅(qū)動(dòng)的切面; type4)注入式 AspectJ 切面(適用于spring 各種version); Attention) A1)前三種都是 spring aop 實(shí)現(xiàn)的變體,spring aop 構(gòu)建在動(dòng)態(tài)代理基礎(chǔ)之上:因此,spring 對(duì) aop的支持局限于方法攔截;(干貨——spring 對(duì) aop的支持局限于方法攔截) A2)spring 借鑒了AspectJ 的切面,以提供注解驅(qū)動(dòng)的AOP。本質(zhì)上,它依然是 spring 基于代理的aop,但是編程模型幾乎與編寫成熟的 AspectJ 注解切面完全一致。這種aop 風(fēng)格的好處在于能夠不使用XML 來完成功能; 3)spring 在運(yùn)行時(shí)通知對(duì)象:通過在代理類中包裹切面,spring 在運(yùn)行期吧切面織入到 spring管理的bean中。 如下圖所示。代理類封裝了目標(biāo)類,并攔截被通知方法的調(diào)用,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標(biāo)bean。當(dāng)代理攔截到方法調(diào)用時(shí),在調(diào)用目標(biāo)bean 方法之前,會(huì)執(zhí)行切面邏輯;
4)spring 只支持方法級(jí)別的連接點(diǎn): 通過使用各種aop 方案可以支持多種連接點(diǎn)模型; 4.1)因?yàn)閟pring基于動(dòng)態(tài)代理,所以spring 只支持方法連接點(diǎn)。這與一些其他的AOP 框架是不同的,例如 AspectJ 和 JBoss,除了方法切點(diǎn)外,它們還提供了字段和構(gòu)造器接入點(diǎn);(干貨——因?yàn)閟pring基于動(dòng)態(tài)代理,所以spring 只支持方法連接點(diǎn)) 4.2)方法攔截可以滿足絕大部分的需求;如果需要方法攔截之外的連接點(diǎn)攔截功能,可以利用 Aspect 來補(bǔ)充 spring aop 的功能;
【2】通過切點(diǎn)來選擇連接點(diǎn) 1)關(guān)于spring aop 的AspectJ 切點(diǎn),最重要的一點(diǎn)是:spring僅僅支持AspectJ 切點(diǎn)指示器的一個(gè)子集; 2)review:spring是基于代理的,而某些切點(diǎn)表達(dá)式是與基于代理的aop 無關(guān)的,下表列出了 spring aop 所支持的 AspectJ 切點(diǎn)指示器;
Attention)只有 execution指示器: 是實(shí)際執(zhí)行匹配的,而其他的指示器都是用來限制匹配的;它是最主要使用的指示器;
【2.1】編寫切點(diǎn) 1)需要有個(gè)主題來定義切面的切點(diǎn): public interface Performance {public void perform(); }2)表達(dá)式execution(* concert.Performance.perform(..)):這個(gè)表達(dá)式能夠設(shè)置當(dāng) perform()方法執(zhí)行時(shí)觸發(fā)通知的調(diào)用;
3)假設(shè)我們需要匹配的切點(diǎn)僅匹配 concert 包,在此case下,可以使用 ?within() 指示器來限制匹配, 如下圖所示;
【2.2】在切點(diǎn)中選擇bean 1)bean()指示器:spring 引入了新的bean()指示器,它允許我們?cè)谇悬c(diǎn)表達(dá)式中使用 bean ?的ID 來標(biāo)識(shí) bean。(干貨——引入指示器的概念) 2)看個(gè)荔枝: execution(* concert.Performance.perform()) ?and bean('jaychou') :我們希望在執(zhí)行Performance.perform方法時(shí)應(yīng)用通知,但限定bean的ID為?jaychou; execution(* concert.Performance.perform()) ?and !bean('jaychou'):?我們希望在執(zhí)行Performance.perform方法時(shí)應(yīng)用通知,但限定bean的ID不為?jaychou;
【3】使用注解創(chuàng)建切面 1)intro:使用注解來創(chuàng)建切面是 AspectJ 5 所引入 的關(guān)鍵特性;
【3.1】定義切面 1)看個(gè)荔枝:將考生定義為一個(gè)切面; @Aspect public class Examinee { // 考生@Before("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void checkIdentity() { // 身份審查System.out.println("please show us your admission ticket.");}@Before("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void checkSecurity() { // 安檢System.out.println("please accept electronic equipment inspection.");}@AfterReturning("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void handinPaper() { // 交卷System.out.println("please hand in an examination paper.");}@AfterThrowing("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void cancelQualification() { // 取消考試資格System.out.println("cancel the exam qualification");} } 對(duì)以上代碼的分析(Analysis): A1)spring使用了 AspectJ 注解來聲明通知方法, 如下表所示:
A2)上面的注解都給定了一個(gè)切點(diǎn)表達(dá)式作為它的值,且這4個(gè)切點(diǎn)表達(dá)式都是相同的,其實(shí)它們可以設(shè)置成為不同的切點(diǎn)表達(dá)式;
3)problem+solution: 3.1)problem:相同的切點(diǎn)表達(dá)式出現(xiàn)了四遍,這可是不光彩的事情; 3.2)solution:引入了 @PointCut注解, 該注解能夠在一個(gè) @AspectJ 切面內(nèi)定義可重用個(gè)切點(diǎn); @Aspect public class ExamineeVersion2 {@Pointcut("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void chineseExamination(){}@Before("chineseExamination()")public void checkIdentity() { // 身份審查System.out.println("please show us your admission ticket.");}@Before("chineseExamination()")public void checkSecurity() { // 安檢System.out.println("please accept electronic equipment inspection.");}@AfterReturning("chineseExamination()")public void handinPaper() { // 交卷System.out.println("please hand in an examination paper.");}@AfterThrowing("chineseExamination()")public void cancelQualification() { // 取消考試資格System.out.println("cancel the exam qualification");} } 對(duì)以上代碼的分析(Analysis): A1)在 Examinee中,chineseExamination()方法使用了 @Pointcut 注解,為@Pointcut注解設(shè)置的值是一個(gè)切點(diǎn)表達(dá)式; A2)實(shí)際上擴(kuò)展了切點(diǎn)表達(dá)式語言,這就可以在 任何的切點(diǎn)表達(dá)式中使用?chineseExamination()方法了;
4)problem+solution: 4.1)problem:如果僅僅是以上代碼的話,即便使用了AspectJ 注解,但它也不會(huì)被視為切面,這些注解不會(huì)解析,也不會(huì)創(chuàng)建將其轉(zhuǎn)換為切面的代理; 4.2)solution:如果使用JavaConfig的話,可以在配置類的類級(jí)別上通過使用 @EnableAspectJAutoProxy 注解啟用自動(dòng)代理功能;(干貨——@EnableAspectJAutoProxy 注解的作用) @Configuration @EnableAspectJAutoProxy @ComponentScan public class ExaminationConfig {@Beanpublic Examinee getExaminee() {return new Examinee("xiaotang");} } 4.3)如果使用XML配置類裝配bean的話,使用 spring aop命名空間中的 <aop:aspectj-autoproxy> 元素:
Attention)? A1)不管使用JavaConfig 還是 XML配置來裝配:AspectJ 自動(dòng)代理都會(huì)為使用 @AspectJ 注解的bean創(chuàng)建一個(gè)代理,這個(gè)代理會(huì)圍繞著所有該切面的切點(diǎn)所匹配的bean; A2)在這種case下,將會(huì)為 Examination bean創(chuàng)建一個(gè)代理,Examinee類中的通知方法將會(huì)在 chineseExamination() 方法調(diào)用前后執(zhí)行; A3)spring的 AspectJ 自動(dòng)代理僅僅是使用 @AspectJ 作為創(chuàng)建切面的指導(dǎo),切面依然是基于代理的;本質(zhì)上,它依然是 spring基于代理的切面;
【3.2】創(chuàng)建環(huán)繞通知 1)intro:環(huán)繞通知是最強(qiáng)大的通知類型。它能夠讓你所編寫的邏輯將被通知的目標(biāo)方法完全包裝起來。實(shí)際上就想是在一個(gè)通知方法中通知編寫前置通知和后置通知; 2)看個(gè)荔枝:重寫 Eaxminee 切面; // 考生(就是一個(gè)切面) @Aspect public class ExamineeVersion3 {@Pointcut("execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))")public void chineseExamination() {}@Around("chineseExamination()")public void doChineseExamination(ProceedingJoinPoint jp) {try {System.out.println("please show us your admission ticket.");System.out.println("please accept electronic equipment inspection.");jp.proceed();System.out.println("please hand in an examination paper.");} catch (Throwable e) {System.out.println("cancel the exam qualification");}} } 對(duì)以上代碼的分析(Analysis): A1)@Around注解表明?doChineseExamination() 方法會(huì)作為?chineseExamination() 切點(diǎn)的環(huán)繞通知;(干貨——@Around注解的作用) A2)別忘記調(diào)用 ProceedingJoinPoint.proceed()方法,如果不調(diào)用這個(gè)方法的話,那么你的通知實(shí)際上會(huì)阻塞對(duì)被通知方法的調(diào)用; A3)當(dāng)然,你可以不調(diào)用ProceedingJoinPoint.proceed()方法,你也可以在通知中對(duì)它進(jìn)行多次調(diào)用;
【3.3】處理通知中的參數(shù) 1)除了環(huán)繞通知,我們編寫的其他通知不需要關(guān)注傳遞給被通知方法的任意參數(shù); 2)如果切面所通知的方法確實(shí)有參數(shù)該怎么辦?切面能訪問和使用傳遞給被通知方法的參數(shù)嗎? 3)看個(gè)荔枝:
對(duì)以上代碼的分析(Analysis): A1)這里的不同點(diǎn)在于切點(diǎn)還聲明了要提供給通知方法的參數(shù);
A2)關(guān)注 args(trackNumber))限定符:它表明傳遞給 playTrack()方法的int類型參數(shù)也會(huì)傳遞到通知里面去,參數(shù)的名稱 trackNumber 也與切點(diǎn)方法簽名中的參數(shù)相匹配; A3)這個(gè)參數(shù)會(huì)傳遞到通知方法中:該方法是通過 @Before 注解和命名切點(diǎn) trackPlayed(trackNumber)定義的。切點(diǎn)定義中的參數(shù)與切點(diǎn)方法中的參數(shù)名稱是一樣的,這樣就完成了從命名切點(diǎn)到通知方法的參數(shù)轉(zhuǎn)移;
@Aspect public class ExamineeArgs {private String name;public ExamineeArgs(String name) {this.name = name;}@Pointcut("execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int)) "+ "&& args(examSiteCode)")public void myGetExamineeNumber(int examSiteCode) {}@Before("myGetExamineeNumber(examSiteCode)")public void checkIdentity(int examSiteCode) { // 身份審查System.out.println("@Before: your exam site code is " + examSiteCode);} } 【3.4】通過注解引入新功能 1)intro:切面能夠?yàn)楝F(xiàn)有的方法增加新功能,利用aop概念,切面與可以為 spring bean 添加新方法;
2)當(dāng)引入接口的方法被調(diào)用時(shí),代理會(huì)把此調(diào)用委托給實(shí)現(xiàn)了新接口的某個(gè)其他對(duì)象。實(shí)際上,一個(gè)bean的實(shí)現(xiàn)被拆分到了多個(gè)類中;(干貨——一個(gè)spring bean的實(shí)現(xiàn)被拆分到了多個(gè)類中) 3)借助于aop 的引入功能,可以不必在設(shè)計(jì)上妥協(xié)或侵入性地改變現(xiàn)有的實(shí)現(xiàn)。為了實(shí)現(xiàn)該功能,我們要?jiǎng)?chuàng)建一個(gè)新切面; @Aspect public class ExamineeArgs {private String name;public ExamineeArgs(String name) {this.name = name;}@Pointcut("execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int)) "+ "&& args(examSiteCode)")public void myGetExamineeNumber(int examSiteCode) { }@Before("myGetExamineeNumber(examSiteCode)")public void checkIdentity(int examSiteCode) { // 身份審查System.out.println("@Before: your exam site code is " + examSiteCode);} }// 高考 public class CollegeEntraceExamination {public CollegeEntraceExamination() { }public void chineseExamination() {System.out.println("chinese examination is underway.");}public int getExamineeNumber(int examSiteCode) {return 1000;} } //---------------------------------------我是分割線. @Aspect public class AdditionExaminationIntroducer{@DeclareParents(value="com.spring.chapter4.CollegeEntraceExamination.chineseExamination+",defaultImpl=AdditionExaminationImpl.class)public static AddtionExamination addtionExamination; } public interface AddtionExamination {void doAddtionalExamination(); } public class AdditionExaminationImpl implements AddtionExamination{@Overridepublic void doAddtionalExamination() {System.out.println("do additional questions as possible as you can.");} } 對(duì)以上代碼的分析(Analysis):
A1)這個(gè)切面與以往切面不同的是:它并沒有前置,后置等通知,而是通過 @DeclareParents注解將?AddtionExamination? 接口引入到?CollegeEntraceExamination bean中; A2)@DeclareParents注解由3個(gè)部分組成:(干貨——@DeclareParents注解的作用) part1)value屬性:指定了哪種類型的bean 要引入該接口; part2)defaultImpl:指定了為引入功能提供實(shí)現(xiàn)的類; part3)@DeclareParents注解所標(biāo)注的靜態(tài)屬性:指明了要引入的接口; A3)和其它切面一樣,需要在spring 應(yīng)用中將 AdditionExaminationIntroducer 聲明為 一個(gè) bean; <bean class="com.spring.chapter4.AdditionExaminationIntroducer" /> spring 的自動(dòng)代理機(jī)制將會(huì)獲取到它的聲明,但 spring 發(fā)現(xiàn)一個(gè)bean使用 @Aspect注解時(shí),spring就會(huì)創(chuàng)建一個(gè)代理,然后將調(diào)用委托給被代理bean 或被引入的實(shí)現(xiàn),這取決于調(diào)用的方法屬于被代理的bean 還是屬于被引入的接口;(干貨——這解釋了為什么一個(gè)spring bean的實(shí)現(xiàn)被拆分到了多個(gè)類中的問題)
對(duì)上圖的分析(Analysis):我們看到,即使CollegeEntraceExamination 并沒有實(shí)現(xiàn)?AddtionExamination接口,但是通過創(chuàng)建新切面為前者添加了新方法,CollegeEntraceExamination?就可以調(diào)用了doAddtionalExamination()方法了;
4)面向注解的切面聲明有一個(gè)明顯的劣勢(shì):你必須能夠?yàn)橥ㄖ愄砑幼⒔狻榱俗龅竭@一點(diǎn),必須要有源碼;如果沒有源碼,或不想將 AspectJ 注解放到你的代碼中,可以使用 spring XML 配置文件中聲明切面;(干貨——面向注解的切面聲明有一個(gè)明顯的劣勢(shì))
【4】在XML 中聲明切面 1)之前有這樣一個(gè)rule:基于注解的bean裝配要優(yōu)于基于JavaConfig 的裝配,基于JavaConfig的裝配 要優(yōu)于基于XML的配置裝配; 2)但是,如果需要聲明切面,但又不能為通知類添加注解的時(shí)候,那么就必須轉(zhuǎn)向XML 配置了; 3)spring 的aop 配置元素能夠以非侵入性的方式聲明切面
【4.1】聲明前置和后置通知 1)通過XML 將無注解的 Examinee 聲明為切面 <aop:config><aop:aspect ref="examinee"><aop:beforepointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="checkIdentity"/><aop:beforepointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="checkIdentity"/><aop:after-returningpointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="handinPaper"/><aop:after-throwingpointcut="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))"method="cancelQualification"/></aop:aspect> </aop:config> 2)使用<aop:pointcut> 定義命名切點(diǎn) <aop:config><aop:aspect ref="examinee"><aop:pointcutid="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:beforepointcut-ref="chineseExamination"method="checkIdentity"/><aop:beforepointcut-ref="chineseExamination"method="checkIdentity"/><aop:after-returningpointcut-ref="chineseExamination"method="handinPaper"/><aop:after-throwingpointcut-ref="chineseExamination"method="cancelQualification"/></aop:aspect> </aop:config> 圖片t18
Attention)spring4-config.xml 源代碼如下: <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXML"><aop:pointcut id="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:before pointcut-ref="chineseExamination" method="checkIdentity" /><aop:before pointcut-ref="chineseExamination" method="checkSecurity" /><aop:after-returning pointcut-ref="chineseExamination" method="handinPaper" /><aop:after-throwing pointcut-ref="chineseExamination" method="cancelQualification" /></aop:aspect></aop:config> </beans> 【4.2】在XML配置元素中聲明環(huán)繞通知 step1)attendChineseExamination()方法提供了aop環(huán)繞通知; // 考生(就是一個(gè)切面) public class ExamineeAroundXML {private String name;public ExamineeAroundXML(String name) {this.name = name;}public void attendChineseExamination(ProceedingJoinPoint jp) {try {System.out.println("AroundXML@Before: please show us your admission ticket.");System.out.println("AroundXML@Before: please accept electronic equipment inspection.");jp.proceed();System.out.println("AroundXML@AfterReturn: please hand in an examination paper.");} catch (Throwable e) {System.out.println("AroundXML@AfterThrowing: cancel the exam qualification");}} } step2)在XML 中使用 <aop:around>元素聲明環(huán)繞通知 <aop:config><aop:aspect ref="audience"><aop:pointcutid="chineseExamination"expression="execution(** com.spring.chapter4.CollegeEntraceExamination.chineseExamination(..))" /><aop:aroundpointcut-ref="chineseExamination"method="attendChineseExamination"/></aop:aspect> </aop:config> 圖片t19
【4.3】 為通知傳遞參數(shù)(在XML中將ExamineeXMLArgs 配置為參數(shù)化的切面 ) 圖片t20
@Configuration @ImportResource("classpath:com/spring/chapter4/spring4-args-config.xml") public class ExaminationConfig {@Beanpublic CollegeEntraceExamination getExamination() {return new CollegeEntraceExamination();}@Bean(name="examineeXMLArgs")public ExamineeXMLArgs getExamineeXMLArgs() {return new ExamineeXMLArgs("xiaotang");} } spring4-args-config.xml 的源碼如下:? <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXMLArgs"><aop:pointcut id="getExamineeNumber"expression="execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int))and args(examSiteCode)" /><aop:before pointcut-ref="getExamineeNumber" method="checkIdentity"/></aop:aspect></aop:config> </beans>
【4.4】通過XML為切面引入新功能 圖片(t21)
@Configuration @ImportResource("classpath:com/spring/chapter4/spring4-args-intro-config.xml") public class ExaminationConfig {@Beanpublic CollegeEntraceExamination getExamination() {return new CollegeEntraceExamination();}@Bean(name="examineeXMLArgs")public ExamineeXMLArgs getExamineeXMLArgs() {return new ExamineeXMLArgs("xiaotang");} } spring4-args-intro-config.xml 的源代碼如下: <?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-3.0.xsd "><aop:aspectj-autoproxy /><aop:config><aop:aspect ref="examineeXMLArgs"><aop:pointcut id="getExamineeNumber"expression="execution(* com.spring.chapter4.CollegeEntraceExamination.getExamineeNumber(int))and args(examSiteCode)" /><aop:before pointcut-ref="getExamineeNumber" method="checkIdentity"/></aop:aspect><aop:aspect><aop:declare-parents types-matching="com.spring.chapter4.CollegeEntraceExamination+" implement-interface="com.spring.chapter4.AddtionExamination" default-impl="com.spring.chapter4.AdditionExaminationImpl"<!-- delegate-ref="additionExaminationImpl" 前提是spring容器中有additionExaminationImpl bean--> /></aop:aspect></aop:config> </beans>
總結(jié)
以上是生活随笔為你收集整理的spring(4)面向切面的Spring(AOP)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html怎么用图片做背景(html怎么用
- 下一篇: 宜昌建委网站(宜昌建委备案)