筆記
學習資料來自Spring Tutorial 24 - Introduction to AOP
Video 1
Functional Programming.
Difference between FP and Object Oriented Programming.
問題
但是這里有個問題总寒,并不是所有的處理都可以規(guī)約到某個class里的。
比如每個class都會用到的方法刁赖,例如Logger贬蛙。
一個比較naive的想法是我們可以將這些方法抽象成一個新的class,
然后其他的class引用這個抽象出來的公共class锰提。
抽象成一個Logger類也存在一些問題曙痘。
- 和業(yè)務(wù)類關(guān)聯(lián)過多
- 業(yè)務(wù)類仍然需要調(diào)用Logger類的方法
- 無法一次性修改所有的方法
AOP解決了這個問題。
通過創(chuàng)建一些Aspect立肘,例如LoggingAspect边坤,
來解決這個問題。
這樣我們就不用修改我們的業(yè)務(wù)代碼谅年,業(yè)務(wù)代碼是非常干凈的茧痒。
然后我們通過Aspect Configuration
來讓Aspect和業(yè)務(wù)代碼交互工作。
Aspect允許我們在目標方法的前后wrapping一些特定的代碼融蹂。
實現(xiàn)步驟:
- 編寫Aspect
- 配置Aspect
Video 4
如何實現(xiàn)一個最簡單的AOP配置旺订。
由于這個視頻比較老弄企,2011年的視頻,所以基本上是基于xml進行的配置耸峭。
我查閱了相關(guān)的資料桩蓉,用annotation配置了一遍,
所以這個project是沒有用到任何xml的劳闹。
4.1 創(chuàng)建一個class LoggingAspect
用@Aspect 和 @Component標記這個class院究,
前者聲明這個class是一個aspect類,后者聲明這個class為Spring中的一個bean本涕。
4.2 在LoggingAspect中創(chuàng)建一個method
用@Before標記這個method业汰,
表示這個方法將在某個特定的條件下被執(zhí)行。
這里視頻中使用@Before("execution(public String getName())")菩颖,
表示在遇到滿足getNawme條件的方法被執(zhí)行時样漆,這個被標記的方法會在這之前被執(zhí)行。
4.3 創(chuàng)建class AppConfig
并標記為@Configuration和@EnableAspectJAutoProxy晦闰。
讓Aspect的配置有效化放祟。
That's it!
這樣我們就實現(xiàn)了一個最基本的AOP。
啟動應(yīng)用呻右,然后在網(wǎng)址中輸入localhost:8080/circle/xxx跪妥。
如果可以看到console口輸出了Aspect中的內(nèi)容(Advice run. Get Method called),那么你就成功了声滥。
Video 5
5.1 WildCard.
execution(* get*())好像在我的這個版本的Spring中不管用眉撵,一直報錯。
找到了原因是因為這里的get設(shè)置太廣泛落塑,導(dǎo)致一些內(nèi)置的tomcat的一些類也被aop了纽疟,
由于一些方法無法訪問到所以一直報錯無法啟動
execution(* com.zyd.model.*.get*())
一些通配符的使用方法,
- 返回類型
- 限制package
- 方法名的通配符
- 方法參數(shù)的通配符
使用通配符的目的是讓我們可以一次性匹配多個方法憾赁,因為通常我們希望切入的方法不止一個污朽。
5.2 Pointcut.
通過創(chuàng)建Pointcut我們可以抽象出一些切入點,并且其他需要做AOP的方法就不需要自己定義何時執(zhí)行龙考,
而是直接調(diào)用引用定義好的Pointcut就行膘壶。
很明顯,如果我們給每個Aspect方法都創(chuàng)建一個單獨的匹配(execution之類的)洲愤,
那么一旦我們修改了業(yè)務(wù)邏輯中的類或者方法名,
我們想要再修改Aspect中的配置的話會非常的繁瑣顷锰,
如果我們把這些匹配都抽象成Pointcut的話管理起來會非常的方便柬赐。
Video 6
一些關(guān)于Pointcut的配置。
- execution:作用于方法
- within:作用于類
Pointcut的組合官紫。
allGetters() && allCircleMethods()
通過組合用更少的Pointcut設(shè)計出更多的pattern肛宋。
隨著我們的業(yè)務(wù)邏輯越來越復(fù)雜州藕,我們可能會創(chuàng)建出非常復(fù)雜的Pointcut,
對于閱讀代碼的人來說很不友好酝陈,對于維護者來說也很難下手床玻。
如果我們可以將復(fù)雜的Pointcut拆分成多個簡單的Pointcut,并且通過排列組合來復(fù)用這些邏輯沉帮,
很顯然是一個非常好的選擇锈死。
Video 7
JoinPoints。
一般用在方法上穆壕〈#可以獲取到執(zhí)行方法的一些信息。
- toString:執(zhí)行了什么方法
- so on
@Before("args(some-args)")
我們也可以在設(shè)置Pointcut的時候指定目標函數(shù)的執(zhí)行參數(shù)喇勋,
這樣我們可以在Aspect里面獲取到函數(shù)執(zhí)行的參數(shù)值進行一些處理缨该。
有時候我們不光是希望切入到某個方法中,在他的之前或者之后做一些事情川背,
我們同時也希望獲取到目標方法的一些信息贰拿,這樣能更靈活的執(zhí)行我們的Aspect邏輯。
比如日志記錄切入方法的執(zhí)行參數(shù)值熄云,返回類型等等膨更。
那么JoinPoints就給我們提供了這樣一個接口來實現(xiàn)這些功能。
Video 8
- @After
- @AfterReturning
- @AfterThrowing
After稍微特殊一點皱碘,因為函數(shù)執(zhí)行可能會拋出異常询一,
所以我們可以設(shè)置一些pointcut,他們的執(zhí)行條件為要么函數(shù)正常返回癌椿,要么拋出異常健蕊。
@AfterReturning(pointcut='point-cut', returning='returnVal')
通過這樣的方式我們可以獲取到切入方法的返回值來進行處理。
比如在某些場景中踢俄,我們希望日志記錄某些特定方法的返回值缩功,來進行異常排查等等。
同理都办,
@AfterThrowing(pointcut='point-cut', throwing='ex')
我們也可以在Aspect中獲取到目標函數(shù)拋出的異常嫡锌。
Video 9
Around.
可以包裹目標函數(shù),甚至決定是否執(zhí)行目標函數(shù)琳钉。
一般可以用來做filter势木。
但是通常來說,我們要盡可能遵循最小原則歌懒,
如果你知道你的Aspect只需要在目標函數(shù)之后執(zhí)行啦桌,那么用@Before就足夠了。
Video 10
Naming Conversion非常的重要。
因為我們在描述匹配的時候我們是用的字符串匹配甫男,
所以如果一個項目的組員不遵守一定的命名規(guī)則的話且改,
那么很容易會匹配不上某些類或者匹配到一些不想匹配的類。
使用Annotation來反向配置Aspect板驳。
- 編寫Aspect方法
- 編寫自定義的annotation
- 配置Aspect方法又跛,綁定方法到某個annotation上
- 在我們想要應(yīng)用此Aspect方法的業(yè)務(wù)類方法上使用這個annotation
這樣,業(yè)務(wù)類中的方法就和特定的Aspect方法綁定上了若治。(通過annotation)
為什么要這么做慨蓝?
因為有時候我們想要給某些特定的方法綁定Aspect,
但是這些特定的方法很難找到規(guī)律直砂,(意味著我們很難寫出通用的Pointcut)菌仁,
這時候通過annotation來綁定會顯得更加靈活。
但同時也增加了業(yè)務(wù)類的負擔静暂,因為他們要維護Aspect的配置济丘。
Video 11
使用xml進行AOP的配置。
但是作者也強調(diào)說個人更喜歡annotation方式的配置洽蛀,
但是畢竟有一些老項目可能還在使用xml的配置方式摹迷,
所以學習xml配置并無壞處。
Video 12
AOP的底層原理用到了代理模式郊供。
簡單的來說目標類被代理類繼承峡碉,
代理類重寫了目標方法,并且在原始的方法的before和after的部分加入Aspect的邏輯驮审。