成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Spring核心 面向切面 AOP

Winer / 3066人閱讀

摘要:下圖展示了這些概念的關(guān)聯(lián)方式通知切面的工作被稱(chēng)為通知。切面在指定的連接點(diǎn)被織入到目標(biāo)對(duì)象中。該注解表明不僅僅是一個(gè),還是一個(gè)切面。

在軟件開(kāi)發(fā)中,散布于應(yīng)用中多處的功能被稱(chēng)為橫切關(guān)注點(diǎn)(crosscutting concern)。通常來(lái)講,這些橫切關(guān)注點(diǎn)從概念上是與應(yīng)用的業(yè)務(wù)邏輯相分離的(但是往往會(huì)直接嵌入到應(yīng)用的業(yè)務(wù)邏輯之中)。把這些橫切關(guān)注點(diǎn)與業(yè)務(wù)邏輯相分離正是面向切面編程(AOP)所要解決的問(wèn)題

面向切面編程

下圖展現(xiàn)了一個(gè)被劃分為模塊的典型應(yīng)用。每個(gè)模塊的核心功能都是為特定業(yè)務(wù)領(lǐng)域提供服務(wù),但是這些模塊都需要類(lèi)似的輔助功能,例如安全和事務(wù)管理

切面提供了取代繼承和委托的方案,且在很多場(chǎng)景下更清晰簡(jiǎn)潔。使用面向切面編程時(shí),仍然在一個(gè)地方定義通用功能,但是可以通過(guò)聲明的方式定義這個(gè)功能要以何種方式在何處應(yīng)用,而無(wú)需修改受影響的類(lèi)。橫切關(guān)注點(diǎn)可以被模塊化為特殊的類(lèi),這些類(lèi)被稱(chēng)為切面(aspect)

這樣做有兩個(gè)好處:首先,現(xiàn)在每個(gè)關(guān)注點(diǎn)都集中于一個(gè)地方,而不是分散到多處代碼中;其次,服務(wù)模塊更簡(jiǎn)潔,因?yàn)樗鼈冎话饕P(guān)注點(diǎn)(或核心功能)的代碼,而次要關(guān)注點(diǎn)的代碼被轉(zhuǎn)移到切面中

定義AOP術(shù)語(yǔ)

描述切面的常用術(shù)語(yǔ)有通知(advice)、切點(diǎn)(pointcut)和連接點(diǎn)(join point)。下圖展示了這些概念的關(guān)聯(lián)方式

通知(Advice)

切面的工作被稱(chēng)為通知。通知定義了切面是什么以及何時(shí)使用

Spring切面可以應(yīng)用5種類(lèi)型的通知:

前置通知(Before):在目標(biāo)方法被調(diào)用之前調(diào)用通知功能

后置通知(After):在目標(biāo)方法完成之后調(diào)用通知,此時(shí)不會(huì)關(guān)心方法的輸出是什么

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

異常通知(After-throwing):在目標(biāo)方法拋出異常后調(diào)用通知

環(huán)繞通知(Around):通知包裹了被通知的方法,在被通知的方法調(diào)用之前和調(diào)用之后執(zhí)行自定義的行為

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

連接點(diǎn)是在應(yīng)用執(zhí)行過(guò)程中能夠插入切面的一個(gè)點(diǎn)。這個(gè)點(diǎn)可以是調(diào)用方法時(shí)、拋出異常時(shí)、甚至修改一個(gè)字段時(shí)。切面代碼可以利用這些點(diǎn)插入到應(yīng)用的正常流程之中,并添加新的行為

切點(diǎn)(Poincut)

通知定義了切面的“什么”和“何時(shí)”的話(huà),切點(diǎn)定義了“何處”。切點(diǎn)的定義會(huì)匹配通知所要織入的一個(gè)或多個(gè)連接點(diǎn)

通常使用明確的類(lèi)和方法名稱(chēng),或是利用正則表達(dá)式定義所匹配的類(lèi)和方法名稱(chēng)來(lái)指定這些切點(diǎn)。有些AOP框架允許創(chuàng)建動(dòng)態(tài)的切點(diǎn),可以根據(jù)運(yùn)行時(shí)的決策(比如方法的參數(shù)值)來(lái)決定是否應(yīng)用通知

切面(Aspect)

切面是通知和切點(diǎn)的結(jié)合。通知和切點(diǎn)共同定義了切面的全部?jī)?nèi)容——它是什么,在何時(shí)和何處完成其功能

引入(Introduction)

引入允許我們向現(xiàn)有的類(lèi)添加新方法或?qū)傩?。例如,我們可以?chuàng)建一個(gè)Auditable通知類(lèi),該類(lèi)記錄了對(duì)象最后一次修改時(shí)的狀態(tài)。這很簡(jiǎn)單,只需一個(gè)方法,setLastModified(Date),和一個(gè)實(shí)例變量來(lái)保存這個(gè)狀態(tài)。然后,這個(gè)新方法和實(shí)例變量就可以被引入到現(xiàn)有的類(lèi)中,從而可以在無(wú)需修改這些現(xiàn)有的類(lèi)的情況下,讓它們具有新的行為和狀態(tài)

織入(Weaving)

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

編譯期:切面在目標(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é)碼。AspectJ 5的加載時(shí)織入(load-time weaving,LTW)就支持以這種方式織入切面

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

Spring對(duì)AOP的支持

Spring提供了4種類(lèi)型的AOP支持:

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

純POJO切面

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

注入式AspectJ切面(適用于Spring各版本)

Spring通知是Java編寫(xiě)的

Spring所創(chuàng)建的通知都是用標(biāo)準(zhǔn)的Java類(lèi)編寫(xiě)的。而且,定義通知所應(yīng)用的切點(diǎn)通常會(huì)使用注解或在Spring配置文件里采用XML來(lái)編寫(xiě)

AspectJ與之相反。雖然AspectJ現(xiàn)在支持基于注解的切面,但AspectJ最初是以Java語(yǔ)言擴(kuò)展的方式實(shí)現(xiàn)的。這種方式有優(yōu)點(diǎn)也有缺點(diǎn)。通過(guò)特有的AOP語(yǔ)言,我們可以獲得更強(qiáng)大和細(xì)粒度的控制,以及更豐富的AOP工具集,但是我們需要額外學(xué)習(xí)新的工具和語(yǔ)法

Spring在運(yùn)行時(shí)通知對(duì)象

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


Spring的切面由包裹了目標(biāo)對(duì)象的代理類(lèi)實(shí)現(xiàn)。代理類(lèi)處理方法的調(diào)用,執(zhí)行額外的切面邏輯,并調(diào)用目標(biāo)方法

直到應(yīng)用需要被代理的bean時(shí),Spring才創(chuàng)建代理對(duì)象。如果使用的是ApplicationContext的話(huà),在ApplicationContext從BeanFactory中加載所有bean的時(shí)候,Spring才會(huì)創(chuàng)建被代理的對(duì)象。因?yàn)镾pring運(yùn)行時(shí)才創(chuàng)建代理對(duì)象,所以我們不需要特殊的編譯器來(lái)織入Spring AOP的切面

Spring只支持方法級(jí)別的連接點(diǎn)

通過(guò)使用各種AOP方案可以支持多種連接點(diǎn)模型。因?yàn)镾pring基于動(dòng)態(tài)代理,所以Spring只支持方法連接點(diǎn)。Spring缺少對(duì)字段連接點(diǎn)的支持,無(wú)法讓我們創(chuàng)建細(xì)粒度的通知,例如攔截對(duì)象字段的修改。而且它不支持構(gòu)造器連接點(diǎn),我們就無(wú)法在bean創(chuàng)建時(shí)應(yīng)用通知。但是方法攔截可以滿(mǎn)足絕大部分的需求。如果需要方法攔截之外的連接點(diǎn)攔截功能,那么可以利用Aspect來(lái)補(bǔ)充Spring AOP的功能

通過(guò)切點(diǎn)來(lái)選擇連接點(diǎn)

關(guān)于Spring AOP的AspectJ切點(diǎn),Spring僅支持AspectJ切點(diǎn)指示器(pointcut designator)的一個(gè)子集。下表列出了Spring AOP所支持的AspectJ切點(diǎn)指示器:

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)型(當(dāng)使用Spring AOP時(shí),方法定義在由指定的注解所標(biāo)注的類(lèi)里)
@annotation 限定匹配帶有指定注解的連接點(diǎn)

在Spring中嘗試使用AspectJ其他指示器時(shí),將會(huì)拋出IllegalArgument-Exception異常

注:只有execution指示器是實(shí)際執(zhí)行匹配的,而其他的指示器都是用來(lái)限制匹配的。說(shuō)明execution指示器是在編寫(xiě)切點(diǎn)定義時(shí)最主要使用的指示器

編寫(xiě)切點(diǎn)
package concert;
public interface Performance
{
    public void perform();
}

Performance可以代表任何類(lèi)型的現(xiàn)場(chǎng)表演,如舞臺(tái)劇、電影或音樂(lè)會(huì)。設(shè)想編寫(xiě)Performance的perform()方法觸發(fā)的通知。下圖展現(xiàn)了一個(gè)切點(diǎn)表達(dá)式,這個(gè)表達(dá)式能夠設(shè)置當(dāng)perform()方法執(zhí)行時(shí)觸發(fā)通知的調(diào)用

使用AspectJ切點(diǎn)表達(dá)式來(lái)選擇Performance的perform()方法:

使用execution()指示器選擇Performance的perform()方法。方法表達(dá)式以“*”號(hào)開(kāi)始,表明了不關(guān)心方法返回值的類(lèi)型。然后指定全限定類(lèi)名和方法名。對(duì)于方法參數(shù)列表,使用兩個(gè)點(diǎn)號(hào)(..)表明切點(diǎn)要選擇任意的perform()方法,無(wú)論該方法的入?yún)⑹鞘裁?/p>

設(shè)現(xiàn)需配置的切點(diǎn)僅匹配concert包。在此場(chǎng)景下,可以使用within()指示器限制切點(diǎn)范圍:

and代替“&&”,or代替“||”,not代替“!”

在切點(diǎn)中選擇bean

Spring引入新的bean()指示器,它允許在切點(diǎn)表達(dá)式中使用bean的ID來(lái)標(biāo)識(shí)bean。bean()使用bean ID或bean名稱(chēng)作為參數(shù)來(lái)限制切點(diǎn)只匹配特定的bean

執(zhí)行Performance的perform()方法時(shí)應(yīng)用通知,但限定bean的ID為woodstock:

execution(* concert.Performance.perform()) and bean("woodstock")

使用非操作為除了特定ID以外的其他bean應(yīng)用通知:

execution(* concert.Performance.perform()) and !bean("woodstock")
使用注解創(chuàng)建切面 定義切面
//Audience類(lèi):觀看演出的切面

package concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class Audience
{
    @Before("execution(** concert.Performance.perform(..))")        // 表演之前
    public void silenceCellPhones()
    {
        System.out.println("Silencing cell phones");
    }
    @Before("execution(** concert.Performance.perform(..))")        // 表演之前
    public void takeSeats()
    {
        System.out.println("Taking seats");
    }        
    @AfterReturning("execution(** concert.Performance.perform(..))")        // 表演之后
    public void applause()
    {
        System.out.println("CLAP CLAP CLAP!!!");
    }
    @AfterThrowing("execution(** concert.Performance.perform(..))")        // 表演失敗之后
    public void demandRefound()
    {
        System.out.println("Demanding a refund");
    }
}    

Audience類(lèi)使用@AspectJ注解進(jìn)行了標(biāo)注。該注解表明Audience不僅僅是一個(gè)POJO,還是一個(gè)切面。Audience類(lèi)中的方法都使用注解來(lái)定義切面的具體行為

Spring使用AspectJ注解來(lái)聲明通知方法:

注  解 通  知
@After 通知方法會(huì)在目標(biāo)方法返回或拋出異常后調(diào)用
@AfterReturning 通知方法會(huì)在目標(biāo)方法返回后調(diào)用
@AfterThrowing 通知方法會(huì)在目標(biāo)方法拋出異常后調(diào)用
@Around 通知方法會(huì)將目標(biāo)方法封裝起來(lái)
@Before 通知方法會(huì)在目標(biāo)方法調(diào)用之前執(zhí)行

為@Pointcut注解設(shè)置的值是一個(gè)切點(diǎn)表達(dá)式,就像之前在通知注解上所設(shè)置的那樣。通過(guò)在performance()方法上添加@Pointcut注解,實(shí)際上擴(kuò)展了切點(diǎn)表達(dá)式語(yǔ)言,這樣就可以在任何的切點(diǎn)表達(dá)式中使用performance()了,如果不這樣做的話(huà),需要在這些地方使用那個(gè)更長(zhǎng)的切點(diǎn)表達(dá)式

現(xiàn)在把所有通知注解中的長(zhǎng)表達(dá)式都替換成了performance(),該方法的實(shí)際內(nèi)容并不重要,在這里實(shí)際上是空的。其實(shí)該方法本身只是一個(gè)標(biāo)識(shí),供@Pointcut注解依附

// 通過(guò)@Pointcut注解聲明頻繁使用的切點(diǎn)表達(dá)式

package concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class Audience
{
    @Pointcut("execution(** concert.Performance.perform(..))")        //定義命名的切點(diǎn)
    public void performance(){}
    
    @Before("performance()") 
    public void silenceCellPhones()
    {
        System.out.println("Silencing cell phones");
    }
    @Before("execution(** concert.Performance.perform(..))")        // 表演之前
    public void takeSeats()
    {
        System.out.println("Taking seats");
    }        
    @AfterReturning("performance()")        // 表演之后
    public void applause()
    {
        System.out.println("CLAP CLAP CLAP!!!");
    }
    @AfterThrowing("performance()")        // 表演失敗之后
    public void demandRefound()
    {
        System.out.println("Demanding a refund");
    }
}       

像其他的Java類(lèi)一樣,它可以裝配為Spring中的bean:

@Bean 
public Audience audience()
{
    return new Audience();
}

通過(guò)上述操作,Audience只會(huì)是Spring容器中的一個(gè)bean。即便使用了AspectJ注解,但它并不會(huì)被視為切面,這些注解不會(huì)解析,也不會(huì)創(chuàng)建將其轉(zhuǎn)換為切面的代理

使用JavaConfig可以在配置類(lèi)的類(lèi)級(jí)別上通過(guò)使用EnableAspectJ-AutoProxy注解啟用自動(dòng)代理功能:

package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

// 啟動(dòng)AspectJ自動(dòng)代理
@Configuration                
@EnableAspectJAutoProxy
@ComponentScan

public class ConcertConfig
{
    @Bean
    public Audience audience()
    {
        return new Audience();
    }
}

使用XML來(lái)裝配bean需要使用Springaop命名空間中的元素

// 在XML中,通過(guò)Spring的aop命名空間啟用AspectJ自動(dòng)代理



    
                    // 啟動(dòng)AspectJ自動(dòng)代理
            // 聲明Audience bean

JavaConfig和XML配置,AspectJ自動(dòng)代理都會(huì)為使用@Aspect注解的bean創(chuàng)建一個(gè)代理,這個(gè)代理會(huì)圍繞著所有該切面的切點(diǎn)所匹配的bean。在這種情況下,將會(huì)為Concert bean創(chuàng)建一個(gè)代理,Audience類(lèi)中的通知方法將會(huì)在perform()調(diào)用前后執(zhí)行

Spring的AspectJ自動(dòng)代理僅僅使用@AspectJ作為創(chuàng)建切面的指導(dǎo),切面依然是基于代理的。在本質(zhì)上,它依然是
Spring基于代理的切面。這一點(diǎn)非常重要,因?yàn)檫@意味著盡管使用的是@AspectJ注解,但我們?nèi)匀幌抻诖矸椒ǖ恼{(diào)用。如果想利用AspectJ的所有能力,我們必須在運(yùn)行時(shí)使用AspectJ并且不依賴(lài)Spring來(lái)創(chuàng)建基于代理的切面

創(chuàng)建環(huán)繞通知

環(huán)繞通知是最為強(qiáng)大的通知類(lèi)型。能夠讓所編寫(xiě)的邏輯被通知的目標(biāo)方法完全包裝起來(lái)。實(shí)際上就像在一個(gè)通知方法中同時(shí)編寫(xiě)前置通知和后置通知

// 使用環(huán)繞通知重新實(shí)現(xiàn)Audience切面
package concert;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class Audience
{
    @Pointcut("execution(** concert.Performance.perform(..))")        //定義命名的切點(diǎn)
    public void performance(){}
    
    @Around("performance()") 
    public void watchPerformance(ProceedingJoinPoint jp)
    {
        try{
            System.out.println("Silencing cell phones");
            System.out.println("Taking seats");
            jp.proceed();
            System.out.println("CLAP CLAP CLAP!!!");
        } catch(Throwable e){
            System.out.println("Demanding a refund");
        }
    }
}           

@Around注解表明watchPerformance()方法會(huì)作為performance()切點(diǎn)的環(huán)繞通知。首先接受ProceedingJoinPoint作為參數(shù)。這個(gè)對(duì)象是必須要有的,因?yàn)樵谕ㄖ行枰ㄟ^(guò)它來(lái)調(diào)用被通知的方法。通知方法中可以做任何的事情,當(dāng)要將控制權(quán)交給被通知的方法時(shí),它需要調(diào)用ProceedingJoinPoint的proceed()方法

注:調(diào)用proceed()方法。如不調(diào)該方法,那么通知實(shí)際上會(huì)阻塞對(duì)被通知方法的調(diào)用;若不調(diào)用proceed()方法,會(huì)阻塞對(duì)被通知方法的訪(fǎng)問(wèn),與之類(lèi)似,也可以在通知中對(duì)它進(jìn)行多次調(diào)用

處理通知中的參數(shù)

創(chuàng)建TrackCounter類(lèi),用來(lái)記錄每個(gè)磁道所播放的次數(shù),是通知playTrack()方法的一個(gè)切面。下面的程序清單展示了這個(gè)切面:

// 使用參數(shù)化的通知來(lái)記錄磁道播放的次數(shù)
package soundsystem;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class TrackCounter
{
    private Map TrackCounts = new HashMap();
    
    // 通知playTrack()方法
    @Pointcut(
        "execution(* soundsystem.CompactDisc.playTrack(int))" +
        "&& args(trackNumber)" )
    public void trackPlayed(int trackNumber){}
    
    @Before("trackPlayed(trackNumber)")            // 在播放前,為該磁道計(jì)數(shù)
    public void countTrack(int trackNumber)
    {
        int currentCount = getPlayCount(trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }
    
    public int getPlayCount(int trackNumber)
    {
        return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
    }
}

在切點(diǎn)表達(dá)式中聲明參數(shù),這個(gè)參數(shù)傳入到通知方法中:

切點(diǎn)表達(dá)式中的args(trackNumber)限定符。表明傳遞給playTrack()方法的int類(lèi)型參數(shù)也會(huì)傳遞到通知中去。參數(shù)的名稱(chēng)trackNumber也與切點(diǎn)方法簽名中的參數(shù)相匹配

這個(gè)參數(shù)會(huì)傳遞到通知方法中,這個(gè)通知方法是通過(guò)@Before注解和命名切點(diǎn)trackPlayed(trackNumber)定義的。切點(diǎn)定義中的參數(shù)與切點(diǎn)方法中的參數(shù)名稱(chēng)是一樣的,這樣就完成了從命名切點(diǎn)到通知方法的參數(shù)轉(zhuǎn)移

// 配置TrackCount記錄每個(gè)磁道播放的次數(shù)
package soundsystem;
import java.util.ArrayList;
import java.List;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy                    // 啟用AspectJ自動(dòng)代理
public class TrackCounterConfig
{
    @Bean
    public CompactDisc sgtPeppers()        // CompactDisc bean
    {
        BlankDisc cd = new BlankDisc();
        cd.setTitle("Sgt. Pepper"s Lonely Hearts Club Band");
        cd.setArtist("The Beales");
        List tracks = new ArrayList();
        tracks.add("Sgt. Pepper"s Lonely Hearts Club Band");
        tracks.add("With a Little Help from My Friends");
        tracks.add("Lucy in the Sky with Diamonds");
        tracks.add("Getting Better");
        tracks.add("Fixing a Hole");
        
        // ...other tracks omitted for brevity...
        cd.setTracks(tracks);
        return cd;
    }
    
    @Bean
    public TrackCounter trackCounter()        // TrackCounter bean
    {
        return new TrackCounter();
    }
}
通過(guò)注解引入新功能

使用Spring AOP,我們可以為bean引入新的方法。代理攔截調(diào)用并委托給實(shí)現(xiàn)該方法的其他對(duì)象

為示例中的所有的Performance實(shí)現(xiàn)引入下面的Encoreable接口:

package concert;
public interface Encoreable
{
    void performEncore();
}

借助于AOP的引入功能,不必在設(shè)計(jì)上妥協(xié)或者侵入性地改變現(xiàn)有的實(shí)現(xiàn)。為了實(shí)現(xiàn)該功能,創(chuàng)建一個(gè)新
的切面:

package concert;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

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

EncoreableIntroducer是一個(gè)切面。但是它與之前所創(chuàng)建的切面不同,并沒(méi)有提供前置、后置或環(huán)繞通知,而是
通過(guò)@DeclareParents注解,將Encoreable接口引入到Performance bean中

@DeclareParents注解由三部分組成:

value屬性指定了哪種類(lèi)型的bean要引入該接口。在本例中,也就是所有實(shí)現(xiàn)Performance的類(lèi)型。(標(biāo)記符后面的加號(hào)表示是Performance的所有子類(lèi)型,而不是Performance本身。)

defaultImpl屬性指定了為引入功能提供實(shí)現(xiàn)的類(lèi)。這里指定的是DefaultEncoreable提供實(shí)現(xiàn)

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

和其他的切面一樣,需要在Spring應(yīng)用中將EncoreableIntroducer聲明為一個(gè)bean:


Spring的自動(dòng)代理機(jī)制將會(huì)獲取到它的聲明,當(dāng)Spring發(fā)現(xiàn)一個(gè)bean使用了@Aspect注解時(shí),Spring就會(huì)創(chuàng)建一個(gè)代理,然后將調(diào)用委托給被代理的bean或被引入的實(shí)現(xiàn),這取決于調(diào)用的方法屬于被代理的bean還是屬于被引入的接口

注入AspectJ切面

為演出創(chuàng)建一個(gè)新切面。具體來(lái)講,以切面的方式創(chuàng)建一個(gè)評(píng)論員的角色,他會(huì)觀看演出并且會(huì)在演出之后提供一些批評(píng)意見(jiàn)

// 使用AspectJ實(shí)現(xiàn)表演的評(píng)論員
package concert;
public aspect CriticAspect{
    public CriticAspect(){}
    
    pointcut performance() : execution(* perform(..));
    afterReturning() : performance()
    {
        System.out.println(criticismEngine.getCriticism());
    }
    private CriticismEngine criticismEngine;
    
    public void setCriticismEngine(CriticismEngine criticismEngine)        // 注入CriticismEngine
    {
        this.criticismEngine = criticismEngine;
    }
}

上述程序中的performance()切點(diǎn)匹配perform()方法。當(dāng)它與afterReturning()通知一起配合使用時(shí),可以讓該切面在表演結(jié)束時(shí)起作用

實(shí)際上,CriticAspect與一個(gè)CriticismEngine對(duì)象相協(xié)作,在表演結(jié)束時(shí),調(diào)用該對(duì)象的getCriticism()方法來(lái)發(fā)表一個(gè)苛刻的評(píng)論。為了避免CriticAspect和CriticismEngine之間產(chǎn)生不必要的耦合,我們通過(guò)Setter依賴(lài)注入為CriticAspect設(shè)置CriticismEngine。此關(guān)系如下圖所示:

切面也需要注入。像其他的bean一樣,Spring可以為AspectJ切面注入依賴(lài)

 要注入到CriticAspect中的CriticismEngine實(shí)現(xiàn)
package com.springinaction.springidol;
public class CriticismEngineImpl implements CriticismEngine
{
    public CriticismEngineImpl(){}
    
    public String getCriticism()
    {
        int i = (int) (Math.random() * criticismPool.length)
        return criticismPool[i];
    }
    
    // injected
    private String[] criticismPool;
    public void setCriticismPool(String[] criticismPool)
    {
        this.criticismPool = criticismPool;
    }
}

CriticismEngineImpl實(shí)現(xiàn)了CriticismEngine接口,通過(guò)從注入的評(píng)論池中隨機(jī)選擇一個(gè)苛刻的評(píng)論

使用XML聲明Spring bean:


    
        
            Worst performance ever!
            I laughed, I cried, then I realized I was at the wrong show.
            A must see show! 
        
    

現(xiàn)在有了一個(gè)要賦予CriticAspect的Criticism-Engine實(shí)現(xiàn)。剩下的就是為CriticAspect裝配CriticismEngineImple。在展示如何實(shí)現(xiàn)注入之前,必須清楚AspectJ切面根本不需要Spring就可以織入到我們的應(yīng)用中。如果想使用Spring的依賴(lài)注入為AspectJ切面注入?yún)f(xié)作者,那我們就需要在Spring配置中把切面聲明為一個(gè)Spring配置中的。如下的聲明會(huì)把criticismEnginebean注入到CriticAspect中:


    

Spring需要通過(guò)aspectOf()工廠(chǎng)方法獲得切面的引用,然后像元素規(guī)定的那樣在該對(duì)象上執(zhí)行依賴(lài)注入

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/67375.html

相關(guān)文章

  • Spring框架之我見(jiàn)(三)——IOC、AOP

    摘要:模塊負(fù)責(zé)的所有面向切面的功能??偨Y(jié)的統(tǒng)一管理,降低了對(duì)象之間的耦合對(duì)主流的框架提供了很好的集成支持提供眾多組件,事務(wù)管理,等具有高度可開(kāi)放性,開(kāi)發(fā)者可以自由選擇部分或全部主要使用工廠(chǎng)模式和代理模式。 聊完了Spring框架中最重要的兩種設(shè)計(jì)模式,我們來(lái)看一下Spring框架的模塊和結(jié)構(gòu)圖。 Spring框架的結(jié)構(gòu) 下圖是Spring官方給出的Spring框架的結(jié)構(gòu)圖。 showImg(...

    khs1994 評(píng)論0 收藏0
  • Spring之旅第七站:面向切面編程(AOP)

    摘要:面向切面的本章主要內(nèi)容面向切面編程的基本原理通過(guò)創(chuàng)建切面使用注解為切面注入依賴(lài)。什么是面向切面編程切面能夠幫我們模塊化橫切關(guān)注點(diǎn)。在使用面向切面編程時(shí),我們?nèi)匀辉谝粋€(gè)地方定義通知功能,而無(wú)需修改受影響的類(lèi)。切面切面是通知和切點(diǎn)的結(jié)合。 面向切面的Spring 本章主要內(nèi)容: 面向切面編程的基本原理 通過(guò)POJO創(chuàng)建切面 使用@Aspect注解 為AspectJ切面注入依賴(lài)。 說(shuō)明 ...

    趙連江 評(píng)論0 收藏0
  • 從源碼入手,一文帶你讀懂Spring AOP面向切面編程

    摘要:,,面向切面編程。,切點(diǎn),切面匹配連接點(diǎn)的點(diǎn),一般與切點(diǎn)表達(dá)式相關(guān),就是切面如何切點(diǎn)。例子中,注解就是切點(diǎn)表達(dá)式,匹配對(duì)應(yīng)的連接點(diǎn),通知,指在切面的某個(gè)特定的連接點(diǎn)上執(zhí)行的動(dòng)作。,織入,將作用在的過(guò)程。因?yàn)樵创a都是英文寫(xiě)的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動(dòng)態(tài)代理嗎?看這篇就夠了》介紹了下...

    wawor4827 評(píng)論0 收藏0
  • SSM

    摘要:核心控制器前端控制器預(yù)處理控制器負(fù)責(zé)接收頁(yè)面請(qǐng)求和返回?cái)?shù)據(jù)給頁(yè)面。使用面向切面編程管理事物日志權(quán)限等。數(shù)據(jù)庫(kù),持久化如何快速學(xué)習(xí)框架的回答知乎其他鏈接 RESTful API 網(wǎng)站即軟件 URI不包含動(dòng)詞 請(qǐng)求方法對(duì)應(yīng) CRUDGET ---SELECT,從服務(wù)器取出資源 POST --- CREATE,在服務(wù)器新建一個(gè)資源 PUT --- UPDATE,在服務(wù)器更新資源(客戶(hù)端提供...

    Pandaaa 評(píng)論0 收藏0
  • Spring入門(mén)IOC和AOP學(xué)習(xí)筆記

    摘要:入門(mén)和學(xué)習(xí)筆記概述框架的核心有兩個(gè)容器作為超級(jí)大工廠(chǎng),負(fù)責(zé)管理創(chuàng)建所有的對(duì)象,這些對(duì)象被稱(chēng)為。中的一些術(shù)語(yǔ)切面切面組織多個(gè),放在切面中定義。 Spring入門(mén)IOC和AOP學(xué)習(xí)筆記 概述 Spring框架的核心有兩個(gè): Spring容器作為超級(jí)大工廠(chǎng),負(fù)責(zé)管理、創(chuàng)建所有的Java對(duì)象,這些Java對(duì)象被稱(chēng)為Bean。 Spring容器管理容器中Bean之間的依賴(lài)關(guān)系,使用一種叫做依賴(lài)...

    wenyiweb 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<