AOP 你看這一篇就夠了

網(wǎng)上很多人在介紹AOP時(shí)都這樣說:面向切面編程,通過預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。個(gè)人認(rèn)為這句話是錯(cuò)誤。AOP和OOP一樣,是一種程序設(shè)計(jì)思想,而非技術(shù)手段。

程序設(shè)計(jì)有六大原則,其中第一原則就是單一職責(zé)原則。意思就是一個(gè)類只負(fù)責(zé)一件事情。這與OOP的封裝特性相得益彰。在這個(gè)條件下,我們的程序會(huì)被分散到不同的類、不同的方法中去。這樣做的好處是降低了類的復(fù)雜性,提高了程序的可維護(hù)性。但是同時(shí),它也使代碼變得啰嗦了。例如,我們要為方法添加調(diào)用日志,那就必須為所有類的所有方法添加日志調(diào)用,盡管它們都是相同的。為了解決上述問題,AOP應(yīng)運(yùn)而生了。

AOP旨在將橫切關(guān)注點(diǎn)與業(yè)務(wù)主體進(jìn)行分類,從而提高程序代碼的模塊化程度。橫切關(guān)注點(diǎn)是一個(gè)抽象的概念,它是指那些在項(xiàng)目中貫穿多個(gè)模塊的業(yè)務(wù)。上個(gè)例子中日志功能就是一個(gè)典型的橫切關(guān)注點(diǎn)。

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

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

動(dòng)態(tài)代理是一種設(shè)計(jì)模式。它有以下特征:

我們不需要自己寫代理類。

運(yùn)行期通過接口直接生成代理對(duì)象。

運(yùn)行期間才確定代理哪個(gè)對(duì)象。

以下面這個(gè)例子為例,我們看一下動(dòng)態(tài)代理的類圖結(jié)構(gòu)。

通常我們的APP都有一部分功能要求用戶登錄之后才能訪問。如修改密碼、修改用戶名等功能。當(dāng)用戶打算使用這些功能時(shí),我們一般要對(duì)用戶的登錄狀態(tài)進(jìn)行判斷,只有用戶登錄了,才能正常使用這些功能。而如果用戶未登錄,我們的APP要跳轉(zhuǎn)到登錄頁。就以修改密碼為例我們看一下動(dòng)態(tài)代理的類圖。

InvocationHandler是Java JDK提供的動(dòng)態(tài)代理的入口,用來對(duì)被代理對(duì)象的方法做處理。

代碼如下:

public static class LoginCheckHandler implements InvocationHandler { private static T proxy(Ssource, Class tClass) {return(T) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{tClass}, new LoginCheckHandler(source)); }? private Object mSource; LoginCheckHandler(Objectsource) { this.mSource =source; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {if(!checkLogin()){ jumpToLoginActivity();returnnull; }returnmethod.invoke(mSource, args); } private booleancheckLogin(){ System.out.println("用戶未登錄");returnfalse; } private voidjumpToLoginActivity(){ System.out.println("跳轉(zhuǎn)到登錄頁"); } } public class Client {? public static void main(String[] args) { IUserSettingsource= new UserSetting(); IUserSetting iUserSetting = LoginCheckHandler.proxy(source,IUserSetting.class); iUserSetting.changePwd("new Password"); } }

經(jīng)過這樣封裝之后,檢查登錄跳轉(zhuǎn)登錄頁的邏輯作為橫切關(guān)注點(diǎn)就和業(yè)務(wù)主體進(jìn)行了分離。當(dāng)有新的需求需要登錄檢查時(shí),我們只需要通過LoginCheckHandler生成新的代理對(duì)象即可。

APT

APT(Annotation Processing Tool)是一種編譯期注解處理技術(shù)。它通過定義注解和處理器來實(shí)現(xiàn)編譯期生成代碼的功能,并且將生成的代碼和源代碼一起編譯成.class文件。通過APT技術(shù),我們將橫切關(guān)注點(diǎn)封裝到注解處理器中,從而實(shí)現(xiàn)橫切關(guān)注點(diǎn)與業(yè)務(wù)主體的分離。更詳細(xì)的介紹請(qǐng)移步

Android編譯期插樁,讓程序自己寫代碼(一)

AspectJ

AspectJ就是一種編譯器,它在Java編譯器的基礎(chǔ)上增加了關(guān)鍵字識(shí)別和編譯方法。因此,AspectJ可以編譯Java代碼。它還提供了Aspect程序。在編譯期間,將開發(fā)者編寫的Aspect程序織入到目標(biāo)程序中,擴(kuò)展目標(biāo)程序的功能。開發(fā)者通過編寫AspectJ程序?qū)崿F(xiàn)AOP功能。更詳細(xì)的介紹請(qǐng)移步

Android編譯期插樁,讓程序自己寫代碼(二)

。

Transform + Javassist/ASM

Transform是Android Gradle提供的,可以操作字節(jié)碼的一種方式。App編譯時(shí),源代碼首先會(huì)被編譯成class,然后再被編譯成dex。在class編譯成dex的過程中,會(huì)經(jīng)過一系列 Transform 處理。Javassist/ASM是一個(gè)能夠非常方便操作字節(jié)碼的庫。我們通過它們可以修改編譯的.class文件。

橫切關(guān)注點(diǎn)

影響應(yīng)用多處的功能(日志、事務(wù)、安全)

增強(qiáng)(Advice)

增強(qiáng)定義了切面要完成的功能以及什么時(shí)候執(zhí)行這個(gè)功能。

Spring 切面可以應(yīng)用 5 種類型的增強(qiáng):

前置增強(qiáng)(Before) 在目標(biāo)方法被調(diào)用前調(diào)用增強(qiáng)功能

后置增強(qiáng)(After) 在目標(biāo)方法完成之后調(diào)用增強(qiáng),不關(guān)注方法輸出是什么

返回增強(qiáng)(After-returning) 在目標(biāo)方法成功執(zhí)行之后調(diào)用增強(qiáng)

異常增強(qiáng)(After-throwing) 在目標(biāo)方法拋出異常后調(diào)用增強(qiáng)

環(huán)繞增強(qiáng)(Around) 在被增強(qiáng)的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義行為,即包括前置增強(qiáng)和后置增強(qiáng)。

連接點(diǎn)(Join Point)

應(yīng)用中每一個(gè)有可能會(huì)被增強(qiáng)的點(diǎn)被稱為連接點(diǎn)。

切點(diǎn)(Pointcut)

切點(diǎn)是規(guī)則匹配出來的連接點(diǎn)。

切面(Aspect)

切面是增強(qiáng)和切點(diǎn)的結(jié)合,定義了在何時(shí)和何處完成其功能。

引入(Introduction)

引入允許我們向現(xiàn)有的類中添加新方法和屬性??梢栽诓恍薷默F(xiàn)有的類的情況下,讓類具有新的行為和狀態(tài)。

織入(Weaving)

織入是把切面應(yīng)用到目標(biāo)對(duì)象中并創(chuàng)建新的代理對(duì)象的過程。在目標(biāo)對(duì)象的生命周期里有多個(gè)點(diǎn)可以進(jìn)行織入:

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

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

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

Spring 對(duì) AOP 的支持

Spring 對(duì) AOP 的支持在很多方面借鑒了 AspectJ 項(xiàng)目。目前 Spring 提供了 4 種類型的 AOP 支持:

基于代理的經(jīng)典 AOP

純 POJO 切面

@AspectJ 注解驅(qū)動(dòng)的切面

注入式 AspectJ 切面

Spring AOP 構(gòu)建在動(dòng)態(tài)代理基礎(chǔ)之上,因此 Spring 對(duì) AOP 的支持局限于方法攔截。

運(yùn)行時(shí)增強(qiáng)

通過在代理中包裹切面,Spring 在運(yùn)行期把切面織入到 Spring 管理的 bean 中。代理類封裝了目標(biāo)類,并攔截被增強(qiáng)方法的調(diào)用,再把調(diào)用轉(zhuǎn)發(fā)給真正的目標(biāo) bean。在代理攔截到方法調(diào)用時(shí),在調(diào)用目標(biāo) bean 方法之前,會(huì)執(zhí)行切面邏輯。

直到應(yīng)用需要代理的 bean 時(shí),Spring 才創(chuàng)建代理對(duì)象。如果使用 ApplicationContext 的話,在 ApplicationContext 從 BeanFactory 中加載所有 bean 的時(shí)候,Spring 才會(huì)創(chuàng)建被代理的對(duì)象。

方法級(jí)別的連接點(diǎn)

Spring 基于動(dòng)態(tài)代理實(shí)現(xiàn) AOP,所以 Spring 只支持方法連接點(diǎn)。其他的 AOP 框架比如 AspectJ 與 JBoss,都提供了字段和構(gòu)造器接入點(diǎn),允許創(chuàng)建細(xì)粒度的增強(qiáng)。

切點(diǎn)表達(dá)式

Spring AOP 中,使用 AspectJ 的切點(diǎn)表達(dá)式來定義切點(diǎn)。Spring 只支持 AspectJ 切點(diǎn)指示器(pointcut designator)的一個(gè)子集。

指示器

AspectJ 指示器描述arg( )限制連接點(diǎn)匹配參數(shù)為指定類型的執(zhí)行方法execution( )用于匹配連接點(diǎn)this指定匹配 AOP 代理的 bean 引用的類型target指定匹配對(duì)象為特定的類within( )指定連接點(diǎn)匹配的類型@annotation匹配帶有指定注解的連接點(diǎn)

編寫切點(diǎn)

package concert;public interface Performance { public void perform();}復(fù)制代碼復(fù)制代碼

Performance 類可以代表任何類型的現(xiàn)場(chǎng)表演,比如電影、舞臺(tái)劇等?,F(xiàn)在編寫一個(gè)切點(diǎn)表達(dá)式來限定 perform() 方法執(zhí)行時(shí)觸發(fā)的增強(qiáng)。

execution(* concert.Performance.perform(..))復(fù)制代碼復(fù)制代碼

每個(gè)部分的意義如下圖所示:

也可以引入其他注解對(duì)匹配規(guī)則做進(jìn)一步限制。比如

execution(* concert.Performance.perform(..)) && within(concert.*)復(fù)制代碼復(fù)制代碼

within() 指示器限制了切點(diǎn)僅匹配 concert 包。

Spring 還有一個(gè) bean() 指示器,允許我們?cè)谇悬c(diǎn)表達(dá)式中使用 bean 的 ID 表示 bean。

execution(* concert.Performance.perform(..)) && bean('woodstock')復(fù)制代碼復(fù)制代碼

以上的切點(diǎn)就表示限定切點(diǎn)的 bean 的 ID 為 woodstock 。

給自己的Java技術(shù)交流群打波廣告吧,想要學(xué)習(xí)Java架構(gòu)技術(shù)的朋友可以加我的群:710373545,群內(nèi)每晚都會(huì)有阿里技術(shù)大牛講解的最新Java架構(gòu)技術(shù)。并會(huì)錄制錄播視頻分享在群公告中,作為給廣大朋友的加群的福利——分布式(Dubbo、Redis、RabbitMQ、Netty、RPC、Zookeeper、高并發(fā)、高可用架構(gòu))/微服務(wù)(Spring Boot、Spring Cloud)/源碼(Spring、Mybatis)/性能優(yōu)化(JVM、TomCat、MySQL)

使用注解創(chuàng)建切面

定義切面

在一場(chǎng)演出之前,我們需要讓觀眾將手機(jī)靜音且就座,觀眾在表演之后鼓掌,在表演失敗之后可以退票。在觀眾類中定義這些功能。

@Aspectpublic class Audience {? @Pointcut("execution(* concert.Performance.perform(..)))") public voidperformance(){} @Before("performance()") public voidsilenceCellPhones() { System.out.println("Silencing cell phones"); } @Before("performance()") public voidtakeSeats() { System.out.println("Taking seats"); } @AfterReturning("performance()") public voidapplause() { System.out.println("CLAP CLAP CLAP!!!"); } @AfterThrowing("performance()") public voiddemandRefund() { System.out.println("Demanding a refund"); }}復(fù)制代碼復(fù)制代碼

@AspectJ 注解表名了該類是一個(gè)切面。 @Pointcut 定義了一個(gè)類中可重用的切點(diǎn),寫切點(diǎn)表達(dá)式時(shí),如果切點(diǎn)相同,可以重用該切點(diǎn)。 其余方法上的注解定義了增強(qiáng)被調(diào)用的時(shí)間,根據(jù)注解名可以知道具體調(diào)用時(shí)間。

到目前為止, Audience 仍然只是 Spring 容器中的一個(gè) bean。即使使用了 AspectJ 注解,但是這些注解仍然不會(huì)解析,因?yàn)槟壳斑€缺乏代理的相關(guān)配置。

如果使用 JavaConfig,在配置類的類級(jí)別上使用 @EnableAspectJAutoProxy 注解啟用自動(dòng)代理功能。

@Configuration@EnableAspectJAutoProxy@ComponentScanpublic class ConcertConfig { @Bean public Audienceaudience() {returnnew Audience(); } }復(fù)制代碼復(fù)制代碼

如果使用 xml ,那么需要引入 <aop:aspectj-autoproxy> 元素。

<?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:context="http://www.springframework.org/schema/context" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <context:component-scan base-package="concert"/> <aop:aspectj-autoproxy/> <bean class="concert.Audience"/></beans>

環(huán)繞增強(qiáng)

環(huán)繞增強(qiáng)就像在一個(gè)增強(qiáng)方法中同時(shí)編寫了前置增強(qiáng)和后置增強(qiáng)。

@Aspectpublic class Audience { @Pointcut("execution(* concert.Performance.perform(..)))") public voidperformance(){} @Around("performance()") public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); joinPoint.proceed(); System.out.println("CLAP CLAP CLAP!!!"); } catch (Throwable throwable) { System.out.println("Demanding a refund"); } }}

可以看到,這個(gè)增強(qiáng)達(dá)到的效果與分開寫前置增強(qiáng)與后置增強(qiáng)是一樣的,但是現(xiàn)在所有的功能都位于同一個(gè)方法內(nèi)。 注意該方法接收 ProceedingJoinPoint 作為參數(shù),這個(gè)對(duì)象必須要有,因?yàn)樾枰ㄟ^它來調(diào)用被增強(qiáng)的方法。 注意,在這個(gè)方法中,我們可以控制不調(diào)用 proceed() 方法,從而阻塞對(duì)增強(qiáng)方法的訪問。同樣,我們也可以在增強(qiáng)方法失敗后,多次調(diào)用 proceed() 進(jìn)行重試。

增強(qiáng)方法參數(shù)

修改 Perform#perform() 方法,添加參數(shù)

package concert;public interface Performance { public void perform(int audienceNumbers);}復(fù)制代碼復(fù)制代碼

我們可以通過切點(diǎn)表達(dá)式來獲取被增強(qiáng)方法中的參數(shù)。

@Pointcut("execution(* concert.Performance.perform(int)) && args(audienceNumbers)))") public void performance(int audienceNumbers){}復(fù)制代碼復(fù)制代碼

注意,此時(shí)方法接收的參數(shù)為 int 型, args(audienceNumbers) 指定參數(shù)名為 audienceNumbers ,與切點(diǎn)方法簽名中的參數(shù)匹配,該參數(shù)不一定與增強(qiáng)方法的參數(shù)名一致。

引入增強(qiáng)

切面不僅僅能夠增強(qiáng)現(xiàn)有方法,也能為對(duì)象新增新的方法。 我們可以在代理中暴露新的接口,當(dāng)引入接口的方法被調(diào)用時(shí),代理會(huì)把此調(diào)用委托給實(shí)現(xiàn)了新接口的某個(gè)其他對(duì)象。實(shí)際上,就是一個(gè) bean 的實(shí)現(xiàn)被拆分到多個(gè)類中了。 定義 Encoreable 接口,將其引入到 Performance 的實(shí)現(xiàn)類中。

public interface Encoreable { void performEncore();}

創(chuàng)建一個(gè)新的切面

@Aspectpublic class EncoreableIntroducer { @DeclareParents(value ="concert.Performance+",defaultImpl = DefaultEncoreable.class) public static Encoreable encoreable;}

我們使用了 @Aspect 將 EncoreableIntroducer 標(biāo)記為一個(gè)切面,但是它沒有提供前置、后置或環(huán)繞增強(qiáng)。通過 @DeclareParents 注解將 Encoreable 接口引入到了 Performance bean 中。

@DeclareParents 注解由三部分組成:

value 屬性指定了哪種類型的 bean 要引入該接口。在上述代碼中,類名后面的 + 號(hào)表示是 Performance 的所有子類型,而不是它本身。

defaultImpl 屬性指定了為引入功能提供實(shí)現(xiàn)的類。

@DeclareParents 注解所標(biāo)注的靜態(tài)屬性指明了要引入的接口。

同樣地,我們?cè)?Spring 應(yīng)用中將該類聲明為一個(gè) bean:

<bean class="concert.EncoreableIntroducer" />

Spring 的自動(dòng)代理機(jī)制將會(huì)獲取到它的聲明,并創(chuàng)建相應(yīng)的代理。然后將調(diào)用委托給被代理的 bean 或者被引入的實(shí)現(xiàn),具體取決于調(diào)用的方法屬于被代理的 bean 還是屬于被引入的接口。

在 XML 中聲明切面

更新一下 Audience 類,將它的 AspectJ 注解全部移除。

public class Audience {? public voidsilenceCellPhones() { System.out.println("Silencing cell phones"); } public voidtakeSeats() { System.out.println("Taking seats"); } public voidapplause() { System.out.println("CLAP CLAP CLAP!!!"); } public voiddemandRefund() { System.out.println("Demanding a refund"); }}

聲明前置與后置增強(qiáng)

<?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"> <aop:config> <aop:aspect ref="audience"> <aop:before pointcut="execution(* concert.Performance.perform(..))" method="silenceCellPhone"/> <aop:before pointcut="execution(* concert.Performance.perform(..))" method="takeSeats"/> <aop:after-returning pointcut="execution(* concert.Performance.perform(..))" method="applause"/> <aop:after-throwing pointcut="execution(* concert.Performance.perform(..))" method="demandRefund"/> </aop:aspect> </aop:config></beans>

如上所示,就將一個(gè)普通方法變?yōu)榱嗽鰪?qiáng)。 大多數(shù)的 AOP 配置元素都必須在 <aop:config>元素的上下文內(nèi)使用。元素名基本上都與注解名相對(duì)應(yīng)。 這里,我們同樣將同一個(gè)切點(diǎn)表達(dá)式寫了四遍,將它提取出來。

<?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"> <aop:config> <aop:aspect ref="audience"> <aop:pointcut id="performance" expression="execution(* concert.Performance.perform(..))"/> <aop:before pointcut-ref="performance" method="silenceCellPhone"/> <aop:before pointcut-ref="performance" method="takeSeats"/> <aop:after-returning pointcut-ref="performance" method="applause"/> <aop:after-throwing pointcut-ref="performance" method="demandRefund"/> </aop:aspect> </aop:config></beans>

注意,此時(shí) <aop:pointcut> 標(biāo)簽位于 <aop:aspect> 下層,故只能在該切面中引用。如果想要一個(gè)切點(diǎn)能夠被多個(gè)切面引用,可以將 <aop:aspect> 元素放在 <aop:config> 下第一層。

環(huán)繞增強(qiáng)

定義環(huán)繞增強(qiáng)方法

public class Audience { public void performance(int audienceNumbers){} public void watchPerformance(ProceedingJoinPoint joinPoint) { try { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); joinPoint.proceed(); System.out.println("CLAP CLAP CLAP!!!"); } catch (Throwable throwable) { System.out.println("Demanding a refund"); } }}

在 xml 中使用 <aop:around> 指定方法名與切點(diǎn)即可。

<?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"> <aop:config> <aop:aspect ref="audience"> <aop:pointcut id="performance" expression="execution(* concert.Performance.perform(..))"/> <aop:around pointcut-ref="performance" method="watchPerformance"/> </aop:aspect> </aop:config></beans>

為增強(qiáng)傳遞參數(shù)

獲取參數(shù)主要就在于切點(diǎn)表達(dá)式。

<aop:pointcut id="performance" expression="execution(* concert.Performance.perform(int)) and args(audienceNumbers)"/>

這樣能在 xml 中定位到一個(gè)參數(shù)類型為 int ,參數(shù)名為 audienceNumbers 的切點(diǎn)。 注意在 xml 中使用了 and 代替 && (在 XML 中, & 符號(hào)會(huì)被解析為實(shí)體的開始)。

引入增強(qiáng)

<aop:declare-parents types-matching="concert.Performance+" implement-interface="concert.Encoreable" default-impl="concert.DefaultEncoreable"/>

types-matching 指定了要匹配的類型,與注解中的 value 值功能相同。

注入 AspectJ 切面

AspectJ 切面提供了 Spring AOP 所不能支持的許多類型的切點(diǎn)。 切面很有可能依賴其他類來完成它們的工作。我們可以借助 Spring 的依賴注入把 bean 裝配進(jìn) AspectJ 切面中。

創(chuàng)建一個(gè)新切面。

public aspect CriticAspect { private CriticismEngine criticismEngine; publicCriticAspect() { } pointcut performance():execution(* perform(..)); afterReturning() :performance() { System.out.println(criticismEngine.getCriticism()); } public voidsetCriticismEngine(CriticismEngine criticismEngine) { this.criticismEngine = criticismEngine; }}

注入的 CritismEngine 的實(shí)現(xiàn)類

public class CriticismEngineImple implements CriticismEngine { publicCriticismEngineImple() { } public StringgetCriticism() { int i = (int) (Math.random() * criticismPool.length);returncriticismPool[i]; }? private String[] criticismPool; public voidsetCriticismPool(String[] criticismPool) { this.criticismPool = criticismPool; }}

CriticAspect 主要作用是在表演結(jié)束后為表演發(fā)表評(píng)論。 實(shí)際上, CriticAspect 是調(diào)用了 CriticismEngine 的方法來發(fā)表評(píng)論。通過 setter 依賴注入為 CriticAspect 設(shè)置 CriticismEngine 。

在配置文件中將 CriticismEngine bean 注入到 CriticAspect 中。

<bean class="om.springinaction.springidol.CriticAspect" factory-method="aspectOf"> <property name="criticismEngine" ref="criticismEngine"/> </bean>

一般情況下,Spring bean 由 Spring 容器初始化,但是 AspectJ 切面是由 AspectJ 在運(yùn)行期創(chuàng)建的。所以在運(yùn)行期間,AspectJ 創(chuàng)建好了 CriticAspect 實(shí)例,每個(gè) AspectJ 都會(huì)提供一個(gè)靜態(tài)的 aspectOf() 方法,返回切面的的單例。 使用 factory-method 調(diào)用 aspectOf() 方法向 CriticAspect 中注入 CriticismEngine 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring致力于提供一種方法管理你的業(yè)務(wù)對(duì)象。在大量Java EE的應(yīng)用中,隨處可見Spring。今天我將簡單的...
    JAVA架構(gòu)師的圈子閱讀 1,508評(píng)論 0 16
  • 本章內(nèi)容: 面向切面編程的基本原理 通過POJO創(chuàng)建切面 使用@AspectJ注解 為AspectJ切面注入依賴 ...
    謝隨安閱讀 3,436評(píng)論 0 9
  • AOP實(shí)現(xiàn)可分為兩類(按AOP框架修改源代碼的時(shí)機(jī)): 靜態(tài)AOP實(shí)現(xiàn):AOP框架在編譯階段對(duì)程序進(jìn)行修改,即實(shí)現(xiàn)...
    數(shù)獨(dú)題閱讀 2,408評(píng)論 0 22
  • **** AOP 面向切面編程 底層原理 代理?。?! 今天AOP課程1、 Spring 傳統(tǒng) AOP2、 Spri...
    luweicheng24閱讀 1,517評(píng)論 0 1
  • 本文轉(zhuǎn)自黃勇 本文是《AOP 那點(diǎn)事兒》的續(xù)集。 在上篇中,我們從寫死代碼,到使用代理;從編程式Spring AO...
    樂百事52淑熙閱讀 394評(píng)論 0 1

友情鏈接更多精彩內(nèi)容