AOP筆記

Spring提供了4種類型的AOP支持:

  • 基于代理的經(jīng)典Spring AOP
  • 純POJO切面
  • @AspectJ注解驅(qū)動(dòng)的切面
  • 注入式AspectJ切面(適用于Spring各版本)

Spring僅支持AspectJ切點(diǎn)指示器(pointcut designator)的一個(gè)子集:

AspectJ指示器 描述
arg() 限制連接點(diǎn)匹配參數(shù)為指定類型的執(zhí)行方法
@args() 限制連接點(diǎn)匹配參數(shù)由指定注解標(biāo)注的執(zhí)行方法
execution() 用于匹配是連接點(diǎn)的執(zhí)行方法
this() 限制連接點(diǎn)匹配AOP代理的bean引用為指定類型的類
target 限制連接點(diǎn)匹配目標(biāo)對象為指定類型的類
@target() 限制連接點(diǎn)匹配特定的執(zhí)行對象,這些對象對應(yīng)的類要具有指定類型的注解
within() 限制連接點(diǎn)匹配指定的類型
@within() 限制連接點(diǎn)匹配指定注解所標(biāo)注的類型(當(dāng)時(shí)用Spring AOP時(shí)结闸,方法定義在由指定的注解所標(biāo)注的類里)
@annotation 限定匹配帶有指定注解的連接點(diǎn)

在Spring中使用AspectJ其他指示器時(shí)癣蟋,將會(huì)拋出IllegalArgumentException異常。

切點(diǎn)表達(dá)式(以劇院為例子):

execution(* concert.Performance.perform(..))
*:返回任意類型
concert.Performance:方法所屬的類
.perform(..):方法

僅匹配concert包:
execution(* concert.Performance.perform(..)) && within(concert.*)
可使用關(guān)系符(如&&, ||等)連接够傍, "!"標(biāo)識(shí)not操作濒生。

除上表所列的指示器佩耳,Spring還引入了一個(gè)新的bean()指示器,它允許我們在切點(diǎn)表達(dá)式中使用bean的id來標(biāo)識(shí)bean:
execution(* concert.Performance.perform(..)) && bean('xiyangyang')
指定對id為'xiyangyang'的bean進(jìn)行操作真椿;
execution(* concert.Performance.perform(..)) && 鹃答!bean('xiyangyang')
對所有id不為'xiyangyang'的bean操作。

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

Spring的AspectJ自動(dòng)代理僅僅使用@AspectJ作為創(chuàng)建切面的指導(dǎo)突硝,切面依然是基于代理的测摔。

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

環(huán)繞通知(@Around)是最為強(qiáng)大的通知類型,它能使你編寫的邏輯將被通知的目標(biāo)方法完全包裝起來,就像在一個(gè)通知方法中同時(shí)編寫前置通知和后置通知锋八。
例:

@Aspect
public class Audience{
  @Pointcut("execution(** concert.Performance.perform(..))")
  public void performance(){}

  @Around("performance()") //環(huán)繞通知方法
  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(Thorwable e){
      System.out.println("Demanding a refund");
    }
  }
}

As we can see,@Around 接受ProceedingJoinPoint作為參數(shù)浙于。這個(gè)對象是必須要有的,我們需要在通知中通過它調(diào)用被通知的方法挟纱。通知方法中可以做任何的事情路媚,當(dāng)要將控制權(quán)交給被通知的方法時(shí),他需要調(diào)用ProceedingJointPoint的proceed()方法樊销。這個(gè)方法必須被調(diào)用整慎,否則通知會(huì)阻塞對被通知方法的調(diào)用。

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

假如我們想記錄某方法執(zhí)行的次數(shù)围苫,有兩種方法裤园,一是直接在每次調(diào)用時(shí)記錄使用次數(shù),然而記錄使用次數(shù)和方法本身是不同的關(guān)注點(diǎn)剂府,因此不應(yīng)該屬于方法拧揽。二就是使用切面。
例:

@Aspect
public class TrackCounter{
  private Map<Integer, Integer> trackCounts = 
      new HashMap<Integer, Integer>();

@Pointcut(
    "execution(* soundsystem.CompactDisc.playTrack(int)) " + //通知playTrack()方法
    "&& 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.contaisKey(trackNumber)
        ? trackCounts.get(trackNumber) : 0;
}

}

其中,
"execution(* soundsystem.CompactDisc.playTrack(int)) " + "&& args(trackNumber)"
* : 返回任意類型
soundsystem.CompactDisc:方法所屬的類型
.playTrack:方法
int:接受int類型的參數(shù)
args(trackNumber):指定參數(shù)
這里需要關(guān)注的是args(trackNumber)限定符淤袜。它表明傳給playTrack()方法的int類型參數(shù)也會(huì)傳遞到通知中去。參數(shù)的名稱trackNumber也與竊電方法簽名中的參數(shù)相匹配衰伯。
現(xiàn)在把TrackCounter和要記錄的類定義為bean铡羡,并啟動(dòng)AspectJ代理,就可以記錄播放次數(shù)了意鲸。

知道如何使用切面包裝方法后烦周,可以看看如何通過編寫切面,為被通知的對象引入全新的功能怎顾。

通過注解引入新功能

如果使用代理暴露新接口读慎,切面所通知的bean看起來像是實(shí)現(xiàn)了新的接口,即便底層實(shí)現(xiàn)類并沒有實(shí)現(xiàn)這些接口也無所謂槐雾。
當(dāng)引入接口的方法被調(diào)用時(shí)夭委,代理會(huì)把此調(diào)用委托給實(shí)現(xiàn)了新接口的某個(gè)其他對象。實(shí)際上就是一個(gè)bean的實(shí)現(xiàn)被拆分到了多個(gè)類中募强。
舉個(gè)栗子株灸,為Performance實(shí)現(xiàn)引入Encoreable接口:

public interface Encoreable{
  void performEncore();
}

首先我們創(chuàng)建一個(gè)新切面:

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

與之前的切面不同,EncoreableIntroducer通過的是@DeclareParants注解將Encoreable接口引入到Performance bean中钻注。和其他切面一樣蚂且,我們需要在Spring中將EncoreableIntroducer聲明為一個(gè)bean。
Spring的自動(dòng)代理機(jī)制會(huì)獲取到此聲明幅恋。注解和自動(dòng)代理提供了一種很便利的方式來創(chuàng)建切面。簡單且涉及極少Spring配置泵肄。但面向注解的切面聲明有一個(gè)明顯劣勢:必須能夠?yàn)橥ㄖ愄砑幼⒔饫弧_@說明必須有源碼淑翼。如果不想將AspectJ放到代碼中,可在Spring XML中配置品追。因?yàn)楣ぷ髦胁挥脁ml玄括,我這里就不說啦。

注入AspectJ切面

如果在執(zhí)行通知時(shí)肉瓦,切面依賴于一個(gè)或多個(gè)類遭京,我們可以在切面內(nèi)部實(shí)例化這些協(xié)作對象。更好的方法是借助Spring的依賴注入把bean裝配進(jìn)AspectJ切面中泞莉。

總結(jié)一下

AOP是OOP的一個(gè)強(qiáng)大補(bǔ)充哪雕。通過AspectJ,我們可以把分散在應(yīng)用各處的行為放入可重用的模塊中鲫趁。通過顯示地聲明在何處如何應(yīng)用該行為斯嚎,可以有效的減少代碼冗余,讓我們的類關(guān)注自身的主要功能挨厚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末堡僻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子疫剃,更是在濱河造成了極大的恐慌钉疫,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巢价,死亡現(xiàn)場離奇詭異陌选,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蹄溉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門咨油,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人柒爵,你說我怎么就攤上這事役电。” “怎么了棉胀?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵法瑟,是天一觀的道長。 經(jīng)常有香客問我唁奢,道長霎挟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任麻掸,我火速辦了婚禮酥夭,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己熬北,他們只是感情好疙描,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著讶隐,像睡著了一般起胰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巫延,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天效五,我揣著相機(jī)與錄音,去河邊找鬼炉峰。 笑死畏妖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的讲冠。 我是一名探鬼主播瓜客,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼竿开!你這毒婦竟也來了谱仪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤否彩,失蹤者是張志新(化名)和其女友劉穎疯攒,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體列荔,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡敬尺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贴浙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片砂吞。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖崎溃,靈堂內(nèi)的尸體忽然破棺而出蜻直,到底是詐尸還是另有隱情,我是刑警寧澤袁串,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布概而,位于F島的核電站,受9級(jí)特大地震影響囱修,放射性物質(zhì)發(fā)生泄漏赎瑰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一破镰、第九天 我趴在偏房一處隱蔽的房頂上張望餐曼。 院中可真熱鬧压储,春花似錦、人聲如沸晋辆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓶佳。三九已至,卻和暖如春鳞青,著一層夾襖步出監(jiān)牢的瞬間霸饲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工臂拓, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厚脉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓胶惰,卻偏偏與公主長得像傻工,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子孵滞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

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