Spring 5 中文解析核心篇-IoC容器之AOP編程(上)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"面向切面的編程(AOP)通過提供另一種思考程序結構的方式來補充面向對像的編程(OOP)。OOP中模塊化的關鍵單元是,而在AOP中模塊化是切面。切面使關注點(例如事務管理)的模塊化可以跨越多種類型和對象。(這種關注在AOP文獻中通常被稱爲“跨領域”關注。)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring的關鍵組件之一是AOP框架。雖然Spring IoC容器不依賴於AOP(這意味着如果你不想使用AOP,就不需要使用AOP),但AOP對Spring IoC進行了補充,提供了一個非常強大的中間件解決方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​  "},{"type":"text","marks":[{"type":"strong"}],"text":"具有AspectJ切入點的Spring AOP"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring提供了使用"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-schema","title":null},"content":[{"type":"text","text":"基於schema"}]},{"type":"text","text":"的方法或"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-ataspectj","title":null},"content":[{"type":"text","text":"@AspectJ註解"}]},{"type":"text","text":"樣式來編寫自定義切面的簡單而強大的方法。這兩種樣式都提供了完全類型化的建議,並使用了AspectJ切入點語言,同時仍然使用Spring AOP進行編織。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本章討論基於"},{"type":"codeinline","content":[{"type":"text","text":"schema"}]},{"type":"text","text":"和基於"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"的AOP支持。"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-api","title":null},"content":[{"type":"text","text":"下一章"}]},{"type":"text","text":"將討論較低級別的AOP支持。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOP在Spring框架中用於:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提供聲明式企業服務。此類服務中最重要的是"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/data-access.html#transaction-declarative","title":null},"content":[{"type":"text","text":"聲明式事務管理"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓用戶實現自定義切面,並用AOP補充其對OOP的使用。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你只對通用聲明性服務或其他預包裝的聲明性中間件服務(例如池)感興趣,則無需直接使用Spring AOP,並且可以跳過本章的大部分內容。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"5.1 AOP概念"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"讓我們首先定義一些主要的AOP概念和術語。這些術語不是特定於Spring的。不幸的是,AOP術語並不是特別直觀。但是,如果使用Spring自己的術語,將會更加令人困惑。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切面:涉及多個類別的關注點的模塊化。事務管理是企業Java應用程序中橫切關注的一個很好的例子。在Spring AOP中,切面是通過使用常規類("},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-schema","title":null},"content":[{"type":"text","text":"基於schema"}]},{"type":"text","text":"的方法)或使用"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解("},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-ataspectj","title":null},"content":[{"type":"text","text":"@AspectJ樣式"}]},{"type":"text","text":")註釋的常規類來實現的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"連接點:程序執行過程中的一點,例如方法的執行或異常的處理。在Spring AOP中,連接點始終代表方法的執行。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通知:切面在特定的連接點處採取的操作。不同類型的通知包括:“"},{"type":"codeinline","content":[{"type":"text","text":"around"}]},{"type":"text","text":"”,“"},{"type":"codeinline","content":[{"type":"text","text":"before"}]},{"type":"text","text":"”和“"},{"type":"codeinline","content":[{"type":"text","text":"after"}]},{"type":"text","text":"”通知。(通知類型將在後面討論。)包括Spring在內的許多AOP框架都將通知建模爲攔截器,並在連接點周圍維護一系列攔截器。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切入點:表示匹配連接點。通知與切入點表達式關聯,並在與該切入點匹配的任何連接點處運行(例如,執行具有特定名稱的方法)。切入點表達式匹配的連接點的概念是AOP的核心,默認情況下,Spring使用"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"切入點表達語言。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引入:在類型上聲明其他方法或字段。Spring AOP允許你向任何通知對象引入新的接口(和相應的實現)。例如,你可以使用引入使Bean實現IsModified接口,以簡化緩存。(引入在"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"社區中稱爲類型間聲明。)"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目標對象:一個或多個切面通知的對象。也稱爲“通知對象”。由於Spring AOP是使用運行時代理實現的,因此該對象始終是代理對象。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"AOP代理:由AOP框架創建的對象,用於實現切面約定(通知方法執行等)。在Spring Framework中,AOP代理是"},{"type":"codeinline","content":[{"type":"text","text":"JDK"}]},{"type":"text","text":"動態代理或"},{"type":"codeinline","content":[{"type":"text","text":"CGLIB"}]},{"type":"text","text":"代理。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"編織:將切面與其他應用程序類型或對象鏈接以創建通知的對象。這可以在編譯時(例如,使用"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編譯器),加載時或在運行時完成。像其他純Java AOP框架一樣,Spring AOP在運行時執行編織。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP包括以下類型的通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前置通知:在連接點之前運行但無法阻止執行流前進到連接點的通知(除非它引發異常)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後置通知:連接點正常完成後要運行的通知(例如,如果某個方法返回而沒有引發異常)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"後置異常通知:如果方法因拋出異常而退出,將執行的通知。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終通知:無論連接點退出的方式如何(正常或異常返回),都將執行通知。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"環繞通知:圍繞連接點的通知,例如方法調用。這是最強大的通知。環繞通知可以在方法調用之前和之後執行自定義行爲。它還負責選擇是繼續到連接點,還是通過返回自己的返回值或拋出異常來簡化通知的方法執行。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"環繞通知是最通用的通知。由於Spring AOP與"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"一樣,提供了各種通知類型,因此我們建議你使用功能最弱的建議類型,以實現所需的行爲。例如,如果你只需要使用方法的返回值更新緩存,則最好使用後置通知而不是環繞通知,儘管環繞通知可以完成相同的事情。使用最具體的通知類型可提供更簡單的編程模型,並減少出錯的可能性。例如,你不需要在用於環繞通知的"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}]},{"type":"text","text":"上調用"},{"type":"codeinline","content":[{"type":"text","text":"proceed()"}]},{"type":"text","text":"方法,因此,你不會失敗。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有通知參數都是靜態類型的,因此你可以使用適當類型(例如,從方法執行返回的值的類型)而不是對象數組的 通知參數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切入點匹配的連接點的概念是AOP的關鍵,它與僅提供攔截功能的舊技術不同。切入點使通知的目標獨立於面向對象的層次結構。例如,你可以將提供聲明性事務管理的環繞通知應用於跨越多個對象(例在服務層中的所有業務操作)的一組方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"5.2 AOP能力和目標"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP是用純Java實現的。不需要特殊的編譯過程。Spring AOP不需要控制類加載器的層次結構,因此適合在Servlet容器或應用程序服務器中使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP當前僅支持方法執行連接點(通知在Spring Bean上執行方法)。儘管可以在不破壞核心Spring AOP API的情況下添加對字段攔截的支持,但並未實現字段攔截。如果需要通知字段訪問和更新連接點,請考慮使用諸如"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"之類的語言。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP的AOP方法不同於大多數其他AOP框架。目的不是提供最完整的AOP實現(儘管Spring AOP相當強大)。相反,其目的是在AOP實現和Spring IoC之間提供緊密的集成,以幫助解決企業應用程序中的常見問題。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,例如,通常將Spring Framework的AOP功能與Spring IoC容器結合使用。通過使用常規bean定義語法來配置切面(儘管這允許強大的“自動代理”功能)。這是與其他AOP實現的關鍵區別。使用Spring AOP不能輕鬆或有效地完成一些事情,比如通知非常細粒度的對象(通常是域對象)。在這種情況下,"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"是最佳選擇。但是,我們的經驗是,Spring AOP爲AOP可以解決的企業Java應用程序中的大多數問題提供了出色的解決方案。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP從未努力與"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"競爭以提供全面的AOP解決方案。我們認爲,基於代理的框架(如Spring AOP)和成熟的框架(如"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":")都是有價值的,它們是互補的,而不是競爭。Spring無縫地將Spring AOP和IoC與"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"集成在一起,以在基於Spring的一致應用程序架構中支持AOP的所有功能。這種集成不會影響Spring AOP API或AOP "},{"type":"codeinline","content":[{"type":"text","text":"Alliance"}]},{"type":"text","text":"API。Spring AOP仍然向後兼容。請參閱"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-api","title":null},"content":[{"type":"text","text":"下一章"}]},{"type":"text","text":",以討論Spring AOP API。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring框架的中心宗旨之一是非侵入性。這就是不應該強迫你將特定於框架的類和接口引入你的業務或領域模型的思想。但是,在某些地方,Spring Framework確實爲你提供了將特定於Spring Framework的依賴項引入代碼庫的選項。提供此類選項的理由是,在某些情況下,以這種方式閱讀或編碼某些特定功能可能會變得更加容易。但是,Spring框架(幾乎)總是爲你提供選擇:你可以自由地就哪個選項最適合你的特定用例或場景做出明智的決定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"與本章相關的一種選擇是選擇哪種AOP框架(以及哪種AOP樣式)。你可以選擇"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"和或Spring AOP。你也可以選擇"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"註解樣式方法或Spring XML配置樣式方法。本章選擇首先介紹"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"風格的方法,這不能表明Spring比Spring XML配置風格更喜歡"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"註釋風格的方法(備註:使用"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編寫例子不能說明Spring更喜歡"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"註解編程)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有關每種樣式的“來龍去脈”的更完整討論,請參見"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-choosing","title":null},"content":[{"type":"text","text":"選擇要使用的AOP聲明樣式"}]},{"type":"text","text":"。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"5.3 AOP代理"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP默認將標準JDK動態代理用於AOP代理。這使得可以代理任何接口(或一組接口)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP也可以使用CGLIB代理。這對於代理類而不是接口是必需的。默認情況下,如果業務對象未實現接口,則使用CGLIB。由於對接口而不是對類進行編程是一種好習慣,因此業務類通常實現一個或多個業務接口。在某些情況下(可能極少發生),你需要通知在接口上未聲明的方法,或需要將代理對象作爲具體類型傳遞給方法,則可以"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-proxying","title":null},"content":[{"type":"text","text":"強制使用CGLIB"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"掌握Spring AOP是基於代理的這一事實很重要。請參閱"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-understanding-aop-proxies","title":null},"content":[{"type":"text","text":"瞭解AOP代理"}]},{"type":"text","text":"以全面瞭解此實現細節的實際含義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":5},"content":[{"type":"text","text":"5.4 @AspectJ支持"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"是一種將切面聲明爲帶有註解的常規Java類的樣式。"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"樣式是"},{"type":"link","attrs":{"href":"https://www.eclipse.org/aspectj","title":null},"content":[{"type":"text","text":"AspectJ項目"}]},{"type":"text","text":"在AspectJ 5版本中引入的。Spring使用"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"提供的用於切入點解析和匹配的庫來解釋與AspectJ 5相同的註解。但是,AOP運行時仍然是純Spring AOP,並且不依賴於"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編譯器或編織器。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編譯器和編織器可以使用完整的"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"語言,有關"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-using-aspectj","title":null},"content":[{"type":"text","text":"在Spring Applications中使用AspectJ"}]},{"type":"text","text":"進行了討論。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.1 激活"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}],"marks":[{"type":"strong"}]},{"type":"text","marks":[{"type":"strong"}],"text":"支持"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要在Spring配置中使用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"切面,你需要啓用Spring支持以基於"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"切面配置Spring AOP,並根據這些切面是否通知對Bean進行自動代理。通過自動代理,我們的意思是,如果Spring確定一個或多個切面通知一個bean,它會自動爲該bean生成一個代理來攔截方法調用並確保按需執行通知。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以使用XML或Java樣式的配置來啓用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"支持。無論哪種情況,你都需要確保"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"的"},{"type":"codeinline","content":[{"type":"text","text":"Aspectjweaver.jar"}]},{"type":"text","text":"庫位於應用程序的類路徑(版本1.8或更高版本)上。該庫在"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"發行版的"},{"type":"codeinline","content":[{"type":"text","text":"lib"}]},{"type":"text","text":"目錄中或從Maven Central存儲庫中獲取。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通過Java配置激活"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}],"marks":[{"type":"strong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過Java "},{"type":"codeinline","content":[{"type":"text","text":"@Configuration"}]},{"type":"text","text":"啓用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"支持,請添加"},{"type":"codeinline","content":[{"type":"text","text":"@EnableAspectJAutoProxy"}]},{"type":"text","text":"註解,如以下示例所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Configuration\n@EnableAspectJAutoProxy\npublic class AppConfig {\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通過XML配置激活"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}],"marks":[{"type":"strong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過基於XML的配置啓用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"支持,請使用"},{"type":"codeinline","content":[{"type":"text","text":""}]},{"type":"text","text":"元素,如以下示例所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假定你使用"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#xsd-schemas","title":null},"content":[{"type":"text","text":"基於XML Schema的配置"}]},{"type":"text","text":"中所述的架構支持。有關如何在aop名稱空間中導入標籤的信息,請參見"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#xsd-schemas-aop","title":null},"content":[{"type":"text","text":"AOP schema"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.2 聲明一個切面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"啓用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"支持後,Spring會自動檢測在應用程序上下文中使用"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"切面(有"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解)的類定義的任何bean,並用於配置Spring AOP。接下來的兩個示例顯示了一個不太有用的切面所需的最小定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"兩個示例中的第一個示例顯示了應用程序上下文中的常規bean定義,該定義指向具有"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解的bean類:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n \n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這兩個示例中的第二個示例顯示了"},{"type":"codeinline","content":[{"type":"text","text":"NotVeryUsefulAspect"}]},{"type":"text","text":"類定義,該類定義使用"},{"type":"codeinline","content":[{"type":"text","text":"org.aspectj.lang.annotation.Aspect"}]},{"type":"text","text":"註解進行註釋;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package org.xyz;\nimport org.aspectj.lang.annotation.Aspect;\n\n@Aspect\npublic class NotVeryUsefulAspect {\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切面(使用"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解的類)可以具有方法和字段,與任何其他類相同。它們還可以包含切入點、通知和引入(類型間)聲明。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通過組件掃描自動檢測切面"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以將切面類註冊爲Spring XML配置中的常規bean,也可以通過類路徑掃描自動檢測它們-與其他任何Spring管理的bean一樣。但是,請注意,"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解不足以在類路徑中進行自動檢測。爲此,你需要添加一個單獨的"},{"type":"codeinline","content":[{"type":"text","text":"@Component"}]},{"type":"text","text":"註解(或者,按照Spring的組件掃描程序的規則,有條件的自定義構造型註解)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"向其他切面提供通知"},{"type":"text","text":"?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在Spring AOP中,切面本身不能成爲其他切面的通知目標。類上的"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解將其標記爲一個切面,因此將其從自動代理中排除。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.3 聲明切入點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切入點確定了感興趣的連接點,從而使我們能夠控制何時執行通知。Spring AOP僅支持Spring Bean的方法執行連接點,因此你可以將切入點視爲與Spring Bean上的方法執行匹配。切入點聲明由兩部分組成:一個包含名稱和任何參數的簽名,以及一個切入點表達式,該表達式精確確定我們感興趣的方法執行。在AOP的"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"註解樣式中,常規方法定義提供了切入點簽名,並且使用"},{"type":"codeinline","content":[{"type":"text","text":"@Pointcut"}]},{"type":"text","text":"註解指示了切入點表達式(用作切入點簽名的方法必須具有"},{"type":"codeinline","content":[{"type":"text","text":"void"}]},{"type":"text","text":"返回類型)。一個示例可能有助於使切入點簽名和切入點表達式之間的區別變得清晰。下面的示例定義一個名爲"},{"type":"codeinline","content":[{"type":"text","text":"anyOldTransfer"}]},{"type":"text","text":"的切入點,該切入點與任何名爲"},{"type":"codeinline","content":[{"type":"text","text":"transfer"}]},{"type":"text","text":"方法的執行相匹配:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Pointcut(\"execution(* transfer(..))\") // 切入點表達式\nprivate void anyOldTransfer() {} // 切入點方法簽名\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"形成"},{"type":"codeinline","content":[{"type":"text","text":"@Pointcut"}]},{"type":"text","text":"註解的值的切入點表達式是一個常規的AspectJ 5切入點表達式。有關"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"的切入點語言的完整討論,請參見"},{"type":"link","attrs":{"href":"https://www.eclipse.org/aspectj/doc/released/progguide/index.html","title":null},"content":[{"type":"text","text":"AspectJ編程指南"}]},{"type":"text","text":"(以及擴展,包括"},{"type":"link","attrs":{"href":"https://www.eclipse.org/aspectj/doc/released/adk15notebook/index.html","title":null},"content":[{"type":"text","text":"AspectJ 5開發人員手冊"}]},{"type":"text","text":")或有關AspectJ的書籍之一(如《Eclipse AspectJ》或《 AspectJ in Action》 )。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"支持的切入點指示符"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP支持以下在切入點表達式中使用的"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"切入點指示符(PCD):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"execution"}]},{"type":"text","text":": 用於匹配方法執行的連接點。這是使用Spring AOP時要使用的主要切入點指示符。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"within"}]},{"type":"text","text":": 限制對某些類型內的連接點的匹配(使用Spring AOP時在匹配類型內聲明的方法的執行)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"this"}]},{"type":"text","text":":限制匹配到連接點(使用Spring AOP時方法的執行)的匹配,其中bean引用(Spring AOP代理)是給定類型的實例。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"target"}]},{"type":"text","text":": 限制匹配到連接點(使用Spring AOP時方法的執行)的匹配,其中目標對象(代理的應用程序對象)是給定類型的實例。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"args"}]},{"type":"text","text":": 限制匹配到連接點(使用Spring AOP時方法的執行)的匹配,其中參數是給定類型的實例。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@target"}]},{"type":"text","text":": 限制匹配到連接點(使用Spring AOP時方法的執行)的匹配,其中執行對象的類具有給定類型的註釋。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@args"}]},{"type":"text","text":":限制匹配的連接點(使用Spring AOP時方法的執行),其中傳遞的實際參數的運行時類型具有給定類型的註解。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@within"}]},{"type":"text","text":":限制匹配到具有給定註解的類型中的連接點(使用Spring AOP時,使用給定註解在類型中聲明的方法的執行)。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"@annotation"}]},{"type":"text","text":": 將匹配點限制在連接點的主題(Spring AOP中正在執行的方法)具有給定註解的連接點。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​  "},{"type":"text","marks":[{"type":"strong"}],"text":"其他切入點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"完整的"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"切入點語言支持Spring不支持的其他切入點指示符:"},{"type":"codeinline","content":[{"type":"text","text":"call"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"set"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"preinitialization"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"staticinitialization"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"initialization"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"handler"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"adviceexecution"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"withincode"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"cflow"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"cflowbelow"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"if"}]},{"type":"text","text":", "},{"type":"codeinline","content":[{"type":"text","text":"@this"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"@withincode"}]},{"type":"text","text":"(備註:意思是Spring不支持這些指示符)。在Spring AOP解釋的切入點表達式中使用這些切入點指示符會導致拋出"},{"type":"codeinline","content":[{"type":"text","text":"IllegalArgumentException"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP支持的切入點指示符集合可能會在將來的版本中擴展,以支持更多的 "},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"切入點指示符。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於Spring AOP僅將匹配限制爲僅方法執行連接點,因此前面對切入點指示符的討論所給出的定義比在"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編程指南中所能找到的要窄。此外,"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"本身具有基於類型的語義,並且在執行連接點處,"},{"type":"codeinline","content":[{"type":"text","text":"this"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"target"}]},{"type":"text","text":"都引用同一個對象:執行該方法的對象。Spring AOP是基於代理的系統,可區分代理對象本身(綁定到此對象)和代理背後的目標對象(綁定到目標)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於Spring的AOP框架基於代理的性質,因此根據定義,不會攔截目標對象內的調用。對於JDK代理,只能攔截代理上的公共接口方法調用。使用CGLIB,將攔截代理上的公共方法和受保護的方法調用(必要時甚至包可見的方法)。但是,通常應通過公共簽名設計通過代理進行的常見交互。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請注意,切入點定義通常與任何攔截方法匹配。如果嚴格地將切入點設置爲僅公開使用,即使在CGLIB代理方案中通過代理可能存在非公開交互,也需要相應地進行定義。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果你的攔截需要在目標類中包括方法調用甚至構造函數,請考慮使用Spring驅動的"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.7.RELEASE/spring-framework-reference/core.html#aop-aj-ltw","title":null},"content":[{"type":"text","text":"本地AspectJ編織"}]},{"type":"text","text":",而不是Spring的基於代理的AOP框架。這構成了具有不同特徵的AOP使用模式,因此在做出決定之前一定要熟悉編織。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP還支持其他名爲bean的PCD。使用PCD,可以將連接點的匹配限制爲特定的命名Spring Bean或一組命名Spring Bean(使用通配符時)。Bean PCD具有以下形式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"bean(idOrNameOfBean)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"idOrNameOfBean"}]},{"type":"text","text":"標記可以是任何Spring bean的名稱。提供了使用"},{"type":"codeinline","content":[{"type":"text","text":"*"}]},{"type":"text","text":"字符的有限通配符支持,因此,如果爲Spring bean建立了一些命名約定,則可以編寫bean PCD表達式來選擇它們。與其他切入點指示符一樣,bean PCD可以與"},{"type":"codeinline","content":[{"type":"text","text":"&&"}]},{"type":"text","text":"(和)、"},{"type":"codeinline","content":[{"type":"text","text":"||"}]},{"type":"text","text":"(或)、和"},{"type":"codeinline","content":[{"type":"text","text":"!"}]},{"type":"text","text":"(否定)運算符一起使用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Bean PCD僅在Spring AOP中受支持,而在本地"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編織中不受支持。它是"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"定義的標準PCD的特定於Spring的擴展,因此不適用於"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"模型中聲明的切面。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Bean PCD在實例級別(基於Spring bean名稱概念構建)上運行,而不是僅在類型級別(基於編織的AOP受其限制)上運行。基於實例的切入點指示符是Spring基於代理的AOP框架的特殊功能,並且與Spring bean工廠緊密集成,因此可以自然而直接地通過名稱識別特定bean。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"組合切入點表達式"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"&&"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"||"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"!"}]},{"type":"text","text":"組合切入點表達式。你還可以按名稱引用切入點表達式。以下示例顯示了三個切入點表達式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Pointcut(\"execution(public * *(..))\")\nprivate void anyPublicOperation() {} //1\n\n@Pointcut(\"within(com.xyz.someapp.trading..*)\")\nprivate void inTrading() {} //2\n\n@Pointcut(\"anyPublicOperation() && inTrading()\")\nprivate void tradingOperation() {} //3\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":null,"normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"如果方法執行連接點表示任何公共方法的執行,則"},{"type":"codeinline","content":[{"type":"text","text":"anyPublicOperation"}]},{"type":"text","text":"匹配。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果交易模塊中有方法執行,則"},{"type":"codeinline","content":[{"type":"text","text":"inTrading"}]},{"type":"text","text":"匹配。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"如果方法執行代表交易模塊中的任何公共方法,則"},{"type":"codeinline","content":[{"type":"text","text":"tradingOperation"}]},{"type":"text","text":"匹配。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最佳實踐是從較小的命名組件中構建更復雜的切入點表達式,如先前所示。按名稱引用切入點時,將應用常規的Java可見性規則(你可以看到相同類型的private切入點,層次結構中"},{"type":"codeinline","content":[{"type":"text","text":"protected"}]},{"type":"text","text":"的切入點,任何位置的public切入點,等等)。可見性不影響切入點匹配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"共享通用切入點定義"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在企業級應用中,開發人員通常希望從多個方面引用應用程序的模塊和特定的操作集。我們建議爲此定義一個"},{"type":"codeinline","content":[{"type":"text","text":"SystemArchitecture"}]},{"type":"text","text":"切面,以捕獲常見的切入點表達式意圖。這樣的切面通常類似於以下示例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package com.xyz.someapp;\n\nimport org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Pointcut;\n\n@Aspect\npublic class SystemArchitecture {\n\n /**\n * A join point is in the web layer if the method is defined\n * in a type in the com.xyz.someapp.web package or any sub-package\n * under that.\n */\n @Pointcut(\"within(com.xyz.someapp.web..*)\")\n public void inWebLayer() {}\n\n /**\n * A join point is in the service layer if the method is defined\n * in a type in the com.xyz.someapp.service package or any sub-package\n * under that.\n */\n @Pointcut(\"within(com.xyz.someapp.service..*)\")\n public void inServiceLayer() {}\n\n /**\n * A join point is in the data access layer if the method is defined\n * in a type in the com.xyz.someapp.dao package or any sub-package\n * under that.\n */\n @Pointcut(\"within(com.xyz.someapp.dao..*)\")\n public void inDataAccessLayer() {}\n\n /**\n * A business service is the execution of any method defined on a service\n * interface. This definition assumes that interfaces are placed in the\n * \"service\" package, and that implementation types are in sub-packages.\n *\n * If you group service interfaces by functional area (for example,\n * in packages com.xyz.someapp.abc.service and com.xyz.someapp.def.service) then\n * the pointcut expression \"execution(* com.xyz.someapp..service.*.*(..))\"\n * could be used instead.\n *\n * Alternatively, you can write the expression using the 'bean'\n * PCD, like so \"bean(*Service)\". (This assumes that you have\n * named your Spring service beans in a consistent fashion.)\n */\n @Pointcut(\"execution(* com.xyz.someapp..service.*.*(..))\")\n public void businessService() {}\n\n /**\n * A data access operation is the execution of any method defined on a\n * dao interface. This definition assumes that interfaces are placed in the\n * \"dao\" package, and that implementation types are in sub-packages.\n */\n @Pointcut(\"execution(* com.xyz.someapp.dao.*.*(..))\")\n public void dataAccessOperation() {}\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在需要切入點表達式的任何地方引用在此切面定義的切入點。例如,要使服務層具有事務性,你可以編寫以下內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n \n\n\n\n \n \n \n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-schema","title":null},"content":[{"type":"text","text":"基於schema的AOP支持"}]},{"type":"text","text":"中討論了"},{"type":"codeinline","content":[{"type":"text","text":""}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":""}]},{"type":"text","text":"元素。"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/data-access.html#transaction","title":null},"content":[{"type":"text","text":"事務管理"}]},{"type":"text","text":"中討論了事務元素。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"實例"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP用戶可能最常使用"},{"type":"codeinline","content":[{"type":"text","text":"execution"}]},{"type":"text","text":"切入點指示符。執行表達式的格式如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)\n throws-pattern?)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了返回類型模式(前面的代碼片段中的"},{"type":"codeinline","content":[{"type":"text","text":"ret-type-pattern"}]},{"type":"text","text":"),名稱模式("},{"type":"codeinline","content":[{"type":"text","text":"name-pattern"}]},{"type":"text","text":")和參數模式("},{"type":"codeinline","content":[{"type":"text","text":"param-pattern"}]},{"type":"text","text":")以外的所有部分都是可選的。返回類型模式確定要匹配連接點、方法的返回類型必須是什麼。"},{"type":"text","marks":[{"type":"italic"}],"text":"最常用作返回類型模式。它匹配任何返回類型。僅當方法返回給定類型時,標準類型名稱才匹配。名稱模式與方法名稱匹配。你可以將"},{"type":"text","text":"通配"},{"type":"codeinline","content":[{"type":"text","text":"*"}]},{"type":"text","text":"符用作名稱模式的全部或一部分。如果你指定了聲明類型模式,請在其後加上"},{"type":"codeinline","content":[{"type":"text","text":"."}]},{"type":"text","text":"將其加入名稱模式組件。參數模式稍微複雜一些:"},{"type":"codeinline","content":[{"type":"text","text":"()"}]},{"type":"text","text":"匹配不帶參數的方法,而"},{"type":"codeinline","content":[{"type":"text","text":"(..)"}]},{"type":"text","text":"匹配任意數量(零個或多個)的參數。"},{"type":"codeinline","content":[{"type":"text","text":"(*)"}]},{"type":"text","text":"模式與採用任何類型的一個參數的方法匹配。"},{"type":"codeinline","content":[{"type":"text","text":"(*,String)"}]},{"type":"text","text":"與採用兩個參數的方法匹配。第一個可以是任何類型,而第二個必須是字符串。有關更多信息,請查閱AspectJ編程指南的“"},{"type":"link","attrs":{"href":"https://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html","title":null},"content":[{"type":"text","text":"語言語義"}]},{"type":"text","text":"”部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下示例顯示了一些常用的切入點表達式:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何公共方法的執行:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"名稱以"},{"type":"codeinline","content":[{"type":"text","text":"set"}]},{"type":"text","text":"開頭的任何方法的執行:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"AccountService"}]},{"type":"text","text":"接口定義的任何方法的執行:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"service"}]},{"type":"text","text":"包中定義的任何方法的執行:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"service"}]},{"type":"text","text":"包或其子包之一中定義的任何方法的執行:"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"service"}]},{"type":"text","text":"包中的任何連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"service"}]},{"type":"text","text":"包或其子包之一中的任何連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代理實現"},{"type":"codeinline","content":[{"type":"text","text":"AccountService"}]},{"type":"text","text":"接口的任何連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目標對象實現"},{"type":"codeinline","content":[{"type":"text","text":"AccountService"}]},{"type":"text","text":"接口的任何連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何採用單個參數並且在運行時傳遞的參數爲"},{"type":"codeinline","content":[{"type":"text","text":"Serializable"}]},{"type":"text","text":"的連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目標對象具有"},{"type":"codeinline","content":[{"type":"text","text":"@Transactional"}]},{"type":"text","text":"註解的任何連接點(僅在Spring AOP中方法執行):"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你也可以在綁定形式中使用"},{"type":"codeinline","content":[{"type":"text","text":"@target"}]},{"type":"text","text":"。有關如何使註解對象在建議正文中可用的信息,請參見“"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-advice","title":null},"content":[{"type":"text","text":"聲明通知"}]},{"type":"text","text":"”部分。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"目標對象的聲明類型具有"},{"type":"codeinline","content":[{"type":"text","text":"@Transactional"}]},{"type":"text","text":"註解的任何連接點(僅在Spring AOP中方法執行):"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你也可以在綁定形式中使用"},{"type":"codeinline","content":[{"type":"text","text":"@within"}]},{"type":"text","text":"。有關如何使註解對象在通知正文中可用的信息,請參見“"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-advice","title":null},"content":[{"type":"text","text":"聲明通知"}]},{"type":"text","text":"”部分。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何執行方法帶有"},{"type":"codeinline","content":[{"type":"text","text":"@Transactional"}]},{"type":"text","text":"註解的連接點(僅在Spring AOP中是方法執行):"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你也可以在綁定形式中使用"},{"type":"codeinline","content":[{"type":"text","text":"@annotation"}]},{"type":"text","text":"。有關如何使註解對象在通知正文中可用的信息,請參見“"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-advice","title":null},"content":[{"type":"text","text":"聲明通知"}]},{"type":"text","text":"”部分。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何採用單個參數的連接點(僅在Spring AOP中是方法執行),並且傳遞的參數的運行時類型具有"},{"type":"codeinline","content":[{"type":"text","text":"@Classified"}]},{"type":"text","text":"註解:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你也可以在綁定形式中使用"},{"type":"codeinline","content":[{"type":"text","text":"@args"}]},{"type":"text","text":"。請參閱“"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-advice","title":null},"content":[{"type":"text","text":"聲明通知"}]},{"type":"text","text":"”部分,如何使通知對象中的註解對象可用。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"名爲"},{"type":"codeinline","content":[{"type":"text","text":"tradeService"}]},{"type":"text","text":"的Spring bean上的任何連接點(僅在Spring AOP中執行方法):"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring Bean上具有與通配符表達式"},{"type":"codeinline","content":[{"type":"text","text":"* Service"}]},{"type":"text","text":"匹配的名稱的任何連接點(僅在Spring AOP中才執行方法):"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"寫一個好的連接點"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在編譯期間,"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"處理切入點以優化匹配性能。檢查代碼並確定每個連接點是否(靜態或動態)匹配給定的切入點是一個耗時的過程。(動態匹配意味着無法從靜態分析中完全確定匹配,並且在代碼中進行了測試以確定在運行代碼時是否存在實際匹配)。首次遇到切入點聲明時,"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"將其重寫爲匹配過程的最佳形式。這是什麼意思?基本上,切入點以DNF(析取範式)重寫,並且對切入點的組件進行排序,以便首先檢查那些較便宜(消耗最小)的組件。這意味着你不必擔心理解各種切入點指示符的性能,並且可以在切入點聲明中以任何順序提供它們。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"只能使用所告訴的內容。爲了獲得最佳的匹配性能,你應該考慮他們試圖達到的目標,並在定義中儘可能縮小匹配的搜索空間。現有的指示符自然分爲三類之一:同類、作用域和上下文:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Kinded"}]},{"type":"text","text":"指示器選擇特定類型的連接點:"},{"type":"codeinline","content":[{"type":"text","text":"execution"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"get"}]},{"type":"text","text":"、 "},{"type":"codeinline","content":[{"type":"text","text":"set"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"call"}]},{"type":"text","text":"和 "},{"type":"codeinline","content":[{"type":"text","text":"handler"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Scoping"}]},{"type":"text","text":"指示器選擇一組感興趣的連接點(可能是多種類型的):"},{"type":"codeinline","content":[{"type":"text","text":"within"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"withincode"}]}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Contextual"}]},{"type":"text","text":"指示符根據上下文匹配(和可選綁定):"},{"type":"codeinline","content":[{"type":"text","text":"this"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"target"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"@annotation"}]}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"編寫正確的切入點至少應包括前兩種類型("},{"type":"codeinline","content":[{"type":"text","text":"Kinded"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"Scoping"}]},{"type":"text","text":")。你可以包括上下文指示符以根據連接點上下文進行匹配,也可以綁定該上下文以在通知中使用。僅提供"},{"type":"codeinline","content":[{"type":"text","text":"Kinded"}]},{"type":"text","text":"的標識符或僅提供"},{"type":"codeinline","content":[{"type":"text","text":"Contextual"}]},{"type":"text","text":"的標識符是可行的,但是由於額外的處理和分析,可能會影響編織性能(使用的時間和內存)。"},{"type":"codeinline","content":[{"type":"text","text":"Scoping"}]},{"type":"text","text":"指定符的匹配非常快,使用它們意味着"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"可以非常迅速地消除不應進一步處理的連接點組。一個好的切入點應儘可能包括一個切入點。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參考代碼:"},{"type":"codeinline","content":[{"type":"text","text":"com.liyong.ioccontainer.starter.AopIocContiner"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.4 聲明通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通知與切入點表達式關聯,並且在切入點匹配的方法執行之前、之後或周圍運行。切入點表達式可以是對命名切入點的簡單引用,也可以是在適當位置聲明的切入點表達式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"前置通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"@Before"}]},{"type":"text","text":"註解在一個切面中聲明前置通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect\npublic class BeforeExample {\n\n @Before(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\")\n public void doAccessCheck() {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果使用就地切入點表達式,則可以將前面的示例重寫爲以下示例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Before;\n\n@Aspect\npublic class BeforeExample {\n\n @Before(\"execution(* com.xyz.myapp.dao.*.*(..))\")\n public void doAccessCheck() {\n // ...\n }\n\n}\n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"返回通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在當匹配方法正常的執行返回時,返回通知運行。你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"@AfterReturning"}]},{"type":"text","text":"註解進行聲明:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.AfterReturning;\n\n@Aspect\npublic class AfterReturningExample {\n\n @AfterReturning(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\")\n public void doAccessCheck() {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在同一切面內擁有多個通知聲明(以及其他成員)。在這些示例中,我們僅顯示單個通知聲明,以及其中每個通知的效果。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時,你需要在通知正文中訪問返回的實際值。你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"@AfterReturning"}]},{"type":"text","text":"的形式綁定返回值以獲取該訪問,如以下示例所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.AfterReturning;\n\n@Aspect\npublic class AfterReturningExample {\n\n @AfterReturning(\n pointcut=\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\",\n returning=\"retVal\")\n public void doAccessCheck(Object retVal) {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"返回屬性中使用的名稱必須與"},{"type":"codeinline","content":[{"type":"text","text":"advice"}]},{"type":"text","text":"方法中的參數名稱相對應。當方法執行返回時,返回值將作爲相應的參數值傳遞到通知方法。"},{"type":"codeinline","content":[{"type":"text","text":"returning"}]},{"type":"text","text":"也將匹配限制爲僅返回指定類型值的方法執行(在這種情況下爲Object,它匹配任何返回值)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請注意,當使用返回後通知時,不可能返回完全不同的引用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"異常後置通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在拋異常通知後,當匹配的方法執行通過拋出異常退出時運行。你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"@AfterThrowing"}]},{"type":"text","text":"註解進行聲明,如以下示例所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.AfterThrowing;\n\n@Aspect\npublic class AfterThrowingExample {\n\n @AfterThrowing(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\")\n public void doRecoveryActions() {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通常,你希望通知僅在引發給定類型的異常時才運行,並且你通常還需要訪問通知正文中的引發異常。你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"throwing"}]},{"type":"text","text":"屬性來限制匹配(如果需要)(否則,請使用"},{"type":"codeinline","content":[{"type":"text","text":"Throwable"}]},{"type":"text","text":"作爲異常類型),並將拋出的異常綁定到通知的參數。以下示例顯示瞭如何執行此操作:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.AfterThrowing;\n\n@Aspect\npublic class AfterThrowingExample {\n\n @AfterThrowing(\n pointcut=\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\",\n throwing=\"ex\")\n public void doRecoveryActions(DataAccessException ex) {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"throwing"}]},{"type":"text","text":"屬性中使用的名稱必須與通知方法中的參數名稱相對應。當通過拋出異常退出方法執行時,該異常將作爲相應的參數值傳遞給通知的方法。"},{"type":"codeinline","content":[{"type":"text","text":"throwing"}]},{"type":"text","text":"還將匹配僅限制爲拋出指定類型的異常(在這種情況下爲"},{"type":"codeinline","content":[{"type":"text","text":"DataAccessException"}]},{"type":"text","text":")的方法執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"最終通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當匹配的方法執行退出時,通知(最終)運行。通過使用"},{"type":"codeinline","content":[{"type":"text","text":"@After"}]},{"type":"text","text":"註解聲明它。之後必須準備處理正常和異常返回條件的通知。它通常用於釋放資源和類似目的。以下示例顯示了最終通知的用法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.After;\n\n@Aspect\npublic class AfterFinallyExample {\n\n @After(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation()\")\n public void doReleaseLock() {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"環繞通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後一種通知是環繞通知。環繞通知在匹配方法的執行過程中“環繞”運行。它有機會在方法執行之前和之後執行工作,並確定何時、如何執行,甚至是否真的執行方法。如果需要以線程安全的方式(例如,啓動和停止計時器)在方法執行之前和之後共享狀態,則通常使用環繞通知。始終使用能力最小的通知來滿足你的要求(也就是說,在通知可以使前置通知時,請勿用環繞通知)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過使用"},{"type":"codeinline","content":[{"type":"text","text":"@Around"}]},{"type":"text","text":"註解來聲明環繞通知。通知方法的第一個參數必須是"},{"type":"codeinline","content":[{"type":"text","text":"ProceedingJoinPoint"}]},{"type":"text","text":"類型。在通知的正文中,在"},{"type":"codeinline","content":[{"type":"text","text":"ProceedingJoinPoint"}]},{"type":"text","text":"上調用"},{"type":"codeinline","content":[{"type":"text","text":"proceed()"}]},{"type":"text","text":"會使底層(真正的執行方法)方法執行。"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"方法也可以傳入"},{"type":"codeinline","content":[{"type":"text","text":"Object []"}]},{"type":"text","text":"。數組中的值用作方法執行時的參數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用"},{"type":"codeinline","content":[{"type":"text","text":"Object []"}]},{"type":"text","text":"進行調用時,"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"的行爲與"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編譯器所編譯的"},{"type":"codeinline","content":[{"type":"text","text":"around"}]},{"type":"text","text":" 通知的"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"爲略有不同。對於使用傳統"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"語言編寫的環繞通知,傳遞給"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"的參數數量必須與傳遞給環繞通知的參數數量(而不是基礎連接點採用的參數數量)相匹配,並且傳遞給給定的參數位置會取代該值綁定到的實體的連接點處的原始值(不要擔心,如果這現在沒有意義)。Spring採取的方法更簡單,並且更適合其基於代理的,僅執行的語義。如果你編譯爲Spring編寫的"},{"type":"codeinline","content":[{"type":"text","text":"@AspectJ"}]},{"type":"text","text":"切面,並在"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編譯器和"},{"type":"codeinline","content":[{"type":"text","text":"weaver"}]},{"type":"text","text":"中使用參數進行處理,則只需要意識到這種區別。有一種方法可以在Spring AOP和"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"之間100%兼容,並且在下面有關"},{"type":"link","attrs":{"href":"https://docs.spring.io/spring/docs/5.2.6.RELEASE/spring-framework-reference/core.html#aop-ataspectj-advice-params","title":null},"content":[{"type":"text","text":"通知參數的部分"}]},{"type":"text","text":"中對此進行了討論。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下示例顯示瞭如何使用環繞通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import org.aspectj.lang.annotation.Aspect;\nimport org.aspectj.lang.annotation.Around;\nimport org.aspectj.lang.ProceedingJoinPoint;\n\n@Aspect\npublic class AroundExample {\n\n @Around(\"com.xyz.myapp.SystemArchitecture.businessService()\")\n public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {\n // start stopwatch\n Object retVal = pjp.proceed();\n // stop stopwatch\n return retVal;\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"環繞通知返回的值是該方法的調用者看到的返回值。例如,如果一個簡單的緩存切面有一個值,則它可以從緩存中返回一個值,如果沒有,則調用"},{"type":"codeinline","content":[{"type":"text","text":"proceed()"}]},{"type":"text","text":"。請注意,在環繞通知的正文中,"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"可能被調用一次,多次或完全不被調用。所有這些都是合法的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參考代碼:"},{"type":"codeinline","content":[{"type":"text","text":"com.liyong.ioccontainer.starter.AopIocContiner"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通知參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring提供了完全類型化的通知,這意味着你可以在通知簽名中聲明所需的參數(如我們先前在返回和拋出示例中所看到的),而不是一直使用"},{"type":"codeinline","content":[{"type":"text","text":"Object []"}]},{"type":"text","text":"數組。我們將在本節的後面部分介紹如何使參數和其他上下文值可用於通知主體。首先,我們看一下如何編寫通用通知,以瞭解該通知當前通知的方法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"獲取當前"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}],"marks":[{"type":"strong"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"任何通知方法都可以將"},{"type":"codeinline","content":[{"type":"text","text":"org.aspectj.lang.JoinPoint"}]},{"type":"text","text":"類型的參數聲明爲其第一個參數。請注意,環繞通知聲明"},{"type":"codeinline","content":[{"type":"text","text":"ProceedingJoinPoint"}]},{"type":"text","text":"類型爲第一個參數,該參數是"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}]},{"type":"text","text":"的子類。"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}]},{"type":"text","text":"接口提供了許多有用的方法:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"getArgs()"}]},{"type":"text","text":": 返回方法參數。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"getThis()"}]},{"type":"text","text":": 返回代理對象。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"getTarget()"}]},{"type":"text","text":": 返回目標對象。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"getSignature()"}]},{"type":"text","text":": 返回通知使用的方法的描述。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"toString()"}]},{"type":"text","text":": 打印有關所有通知方法的有用描述。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有關更多詳細信息,請參見"},{"type":"link","attrs":{"href":"https://www.eclipse.org/aspectj/doc/released/runtime-api/org/aspectj/lang/JoinPoint.html","title":null},"content":[{"type":"text","text":"javadoc"}]},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"傳遞參數給通知"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們已經看到了如何綁定返回的值或異常值(在返回之後和引發通知之後)。要使參數值可用於通知正文,可以使用"},{"type":"codeinline","content":[{"type":"text","text":"args"}]},{"type":"text","text":"的綁定形式。如果在"},{"type":"codeinline","content":[{"type":"text","text":"args"}]},{"type":"text","text":"表達式中使用參數名稱代替類型名稱,則在調用通知時會將相應參數的值作爲參數值傳遞。一個例子應該使這一點更清楚。假設你要通知以"},{"type":"codeinline","content":[{"type":"text","text":"Account"}]},{"type":"text","text":"對象作爲第一個參數的"},{"type":"codeinline","content":[{"type":"text","text":"DAO"}]},{"type":"text","text":"操作的執行,並且你需要在通知正文中訪問該帳戶。你可以編寫以下內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)\")\npublic void validateAccount(Account account) {\n // ...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"切入點表達式的"},{"type":"codeinline","content":[{"type":"text","text":"args(account,..)"}]},{"type":"text","text":"部分有兩個用途。首先,它將匹配限制爲僅方法採用至少一個參數且傳遞給該參數的參數爲"},{"type":"codeinline","content":[{"type":"text","text":"Account"}]},{"type":"text","text":"實例的那些方法執行。其次,它通過"},{"type":"codeinline","content":[{"type":"text","text":"account"}]},{"type":"text","text":"參數使實際的"},{"type":"codeinline","content":[{"type":"text","text":"Account"}]},{"type":"text","text":"對象可用於通知。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"寫這個的另一種方法是聲明一個切入點,當它匹配一個連接點時提供"},{"type":"codeinline","content":[{"type":"text","text":"Account"}]},{"type":"text","text":"對象值,然後從通知中引用命名的切入點。如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Pointcut(\"com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)\")\nprivate void accountDataAccessOperation(Account account) {}\n\n@Before(\"accountDataAccessOperation(account)\")\npublic void validateAccount(Account account) {\n // ...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有關更多詳細信息,請參見"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"編程指南。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代理對象("},{"type":"codeinline","content":[{"type":"text","text":"this"}]},{"type":"text","text":")、目標對象("},{"type":"codeinline","content":[{"type":"text","text":"target"}]},{"type":"text","text":")和註解("},{"type":"codeinline","content":[{"type":"text","text":"@within"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"@target"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"@annotation"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"@args"}]},{"type":"text","text":")都可以以類似的方式綁定。接下來的兩個示例顯示如何匹配使用"},{"type":"codeinline","content":[{"type":"text","text":"@Auditable"}]},{"type":"text","text":"註解的方法的執行並提取審計代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這兩個示例中的第一個顯示了"},{"type":"codeinline","content":[{"type":"text","text":"@Auditable"}]},{"type":"text","text":"註解的定義:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Retention(RetentionPolicy.RUNTIME)\n@Target(ElementType.METHOD)\npublic @interface Auditable {\n AuditCode value();\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這兩個示例中的第二個示例顯示了與"},{"type":"codeinline","content":[{"type":"text","text":"@Auditable"}]},{"type":"text","text":"方法的執行相匹配的通知:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(\"com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)\")\npublic void audit(Auditable auditable) {\n AuditCode code = auditable.value();\n // ...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通知參數和泛型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Spring AOP可以處理類聲明和方法參數中使用的泛型。假設你具有如下通用類型:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface Sample {\n void sampleGenericMethod(T param);\n void sampleGenericCollectionMethod(Collection param);\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以通過在要攔截方法的參數類型中鍵入advice參數,將方法類型的攔截限制爲某些參數類型:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(\"execution(* ..Sample+.sampleGenericMethod(*)) && args(param)\")\npublic void beforeSampleMethod(MyType param) {\n // Advice implementation\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種方法不適用於泛型集合。因此,你不能按以下方式定義切入點:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(\"execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)\")\npublic void beforeSampleMethod(Collection param) {\n // Advice implementation\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了使這項工作有效,我們將不得不檢查集合的每個元素,這是不合理的,因爲我們也無法決定通常如何處理"},{"type":"codeinline","content":[{"type":"text","text":"null"}]},{"type":"text","text":"。要實現類似的目的,你必須將參數鍵入"},{"type":"codeinline","content":[{"type":"text","text":"Collection >"}]},{"type":"text","text":"並手動檢查元素的類型。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"確定參數名稱"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通知調用中的參數綁定依賴於切入點表達式中使用的名稱與通知和切入點方法簽名中聲明的參數名稱的匹配。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過Java反射無法獲得參數名稱,因此Spring AOP使用以下策略來確定參數名稱:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果用戶已明確指定參數名稱,則使用指定的參數名稱。通知和切入點註解均具有可選的"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性,你可以使用該屬性來指定帶註解的方法的參數名稱。這些參數名稱在運行時可用。以下示例顯示如何使用"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(value=\"com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)\",\n argNames=\"bean,auditable\")\npublic void audit(Object bean, Auditable auditable) {\n AuditCode code = auditable.value();\n // ... use code and bean\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果第一個參數是"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"ProceedingJoinPoint"}]},{"type":"text","text":"或"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint.StaticPart"}]},{"type":"text","text":"類型,則可以從"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性的值中忽略該參數的名稱。例如,如果你修改前面的通知以接收連接點對象,則"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性不需要包括它:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(value=\"com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)\",\n argNames=\"bean,auditable\")\npublic void audit(JoinPoint jp, Object bean, Auditable auditable) {\n AuditCode code = auditable.value();\n // ... use code, bean, and jp\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint"}]},{"type":"text","text":"、"},{"type":"codeinline","content":[{"type":"text","text":"ProceedingJoinPoint"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"JoinPoint.StaticPart"}]},{"type":"text","text":"類型的第一個參數給予的特殊處理對於不收集任何其他連接點上下文的通知實例特別方便。在這種情況下,你可以省略"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性。例如,以下通知無需聲明"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"屬性:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Before(\"com.xyz.lib.Pointcuts.anyPublicMethod()\")\npublic void audit(JoinPoint jp) {\n // ... use jp\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用'"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"'屬性有點笨拙,因此,如果未指定'"},{"type":"codeinline","content":[{"type":"text","text":"argNames"}]},{"type":"text","text":"'屬性,Spring AOP將查找該類的調試信息,並嘗試從局部變量表中確定參數名稱。只要已使用調試信息(至少是"},{"type":"codeinline","content":[{"type":"text","text":" -g:vars"}]},{"type":"text","text":")編譯了類,此信息就會存在。 啓用此標誌時進行編譯的後果是:(1)你的代碼稍微易於理解(逆向工程),(2)類文件的大小略大(通常無關緊要),(3)編譯器未應用刪除未使用的局部變量的優化。換句話說,通過啓用該標誌,你應該不會遇到任何困難。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果在沒有必要調試信息的情況下編譯了代碼,Spring AOP將嘗試推斷綁定變量與參數的配對(例如,如果切入點表達式中僅綁定了一個變量,並且advice方法僅接受一個參數,則配對很明顯)。如果在給定可用信息的情況下變量的綁定不明確,則拋出"},{"type":"codeinline","content":[{"type":"text","text":"AmbiguousBindingException"}]},{"type":"text","text":"。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果以上所有策略均失敗,則拋出"},{"type":"codeinline","content":[{"type":"text","text":"IllegalArgumentException"}]},{"type":"text","text":"。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"proceed參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"前面我們提到過,我們將描述如何編寫一個在Spring AOP和"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"中始終有效的參數的"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"調用。解決方案是確保通知簽名按順序綁定每個方法參數。以下示例顯示瞭如何執行此操作:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Around(\"execution(List find*(..)) && \" +\n \"com.xyz.myapp.SystemArchitecture.inDataAccessLayer() && \" +\n \"args(accountHolderNamePattern)\")\npublic Object preProcessQueryPattern(ProceedingJoinPoint pjp,\n String accountHolderNamePattern) throws Throwable {\n String newPattern = preProcess(accountHolderNamePattern);\n return pjp.proceed(new Object[] {newPattern});\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在許多情況下,無論如何都要進行此綁定(如上例所示)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"通知順序"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當多條通知都希望在同一連接點上運行時會發生什麼? Spring AOP遵循與"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"相同的優先級規則來確定通知執行的順序。優先級最高的通知在進入時首先運行(因此,給定兩個"},{"type":"codeinline","content":[{"type":"text","text":"before"}]},{"type":"text","text":"通知,優先級最高的通知首先運行)。在從連接點出來的過程中,優先級最高的通知最後運行(因此,給定兩個"},{"type":"codeinline","content":[{"type":"text","text":"after"}]},{"type":"text","text":"通知,優先級最高的通知將排在第二)。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在不同切面定義的兩個通知都需要在同一個連接點上運行時,除非另行指定,否則執行順序是未定義的。你可以通過指定優先級來控制執行順序。通過在切面類中實現"},{"type":"codeinline","content":[{"type":"text","text":"org.springframework.core.Ordered"}]},{"type":"text","text":"接口或使用Order註解對其進行註解,可以通過常規的Spring方法來完成。給定兩個切面,從"},{"type":"codeinline","content":[{"type":"text","text":"Ordered.getValue()"}]},{"type":"text","text":"(或註解值)返回較低值的切面具有較高的優先級"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當在同一個切面中定義的兩個通知都需要在同一個連接點上運行時,順序是未定義的(因爲無法通過java編譯類的反射檢索聲明順序)。考慮將此類通知方法分解爲每個切面類中的每個連接點的一個通知方法,或者將通知片段重構爲可以在切面級別排序的單獨切面類。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.5 引入"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"引入(在AspectJ中稱爲類型間聲明)使能夠聲明已通知的對象實現給定接口,並代表這些對象提供該接口的實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以使用"},{"type":"codeinline","content":[{"type":"text","text":"@DeclareParents"}]},{"type":"text","text":"註解進行介紹。此註解用於聲明匹配類型具有新的父類(因此具有名稱)。例如,給定一個名爲"},{"type":"codeinline","content":[{"type":"text","text":"UsageTracked"}]},{"type":"text","text":"的接口和該接口的一個名爲"},{"type":"codeinline","content":[{"type":"text","text":"DefaultUsageTracked"}]},{"type":"text","text":"的實現,下面的切面聲明瞭服務接口的所有實現者也實現了"},{"type":"codeinline","content":[{"type":"text","text":"UsageTracked"}]},{"type":"text","text":"接口(例如通過"},{"type":"codeinline","content":[{"type":"text","text":"JMX"}]},{"type":"text","text":"公開統計信息):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Aspect\npublic class UsageTracking {\n\n @DeclareParents(value=\"com.xzy.myapp.service.*+\", defaultImpl=DefaultUsageTracked.class)\n public static UsageTracked mixin;\n\n @Before(\"com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)\")\n public void recordUsage(UsageTracked usageTracked) {\n usageTracked.incrementUseCount();\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"要實現的接口由帶註解的字段的類型確定。"},{"type":"codeinline","content":[{"type":"text","text":"@DeclareParents"}]},{"type":"text","text":"註解的"},{"type":"codeinline","content":[{"type":"text","text":"value"}]},{"type":"text","text":"屬性是"},{"type":"codeinline","content":[{"type":"text","text":"AspectJ"}]},{"type":"text","text":"類型的模式。匹配類型的任何bean都實現"},{"type":"codeinline","content":[{"type":"text","text":"UsageTracked"}]},{"type":"text","text":"接口。注意,在前面示例的"},{"type":"codeinline","content":[{"type":"text","text":"before"}]},{"type":"text","text":"通知中,服務bean可以直接用作"},{"type":"codeinline","content":[{"type":"text","text":"UsageTracked"}]},{"type":"text","text":"接口的實現。如果以編程方式訪問bean,則應編寫以下內容:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"UsageTracked usageTracked = (UsageTracked) context.getBean(\"myService\");\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"參考代碼:"},{"type":"codeinline","content":[{"type":"text","text":"com.liyong.ioccontainer.starter.AopDeclareParentsIocContiner"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.6 切面實例化模型"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是一個高級主題。如果你剛開始使用AOP,則可以放心地跳過它,直到以後。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"默認情況下,應用程序上下文中每個切面都有一個實例。 AspectJ將此稱爲單例實例化模型。可以使用bean生命週期來定義切面。Spring支持AspectJ的"},{"type":"codeinline","content":[{"type":"text","text":"perthis"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"pertarget"}]},{"type":"text","text":"實例化模型(當前不支持"},{"type":"codeinline","content":[{"type":"text","text":"percflow"}]},{"type":"text","text":","},{"type":"codeinline","content":[{"type":"text","text":"percflowbelow"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"pertypewithin"}]},{"type":"text","text":")。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以通過在"},{"type":"codeinline","content":[{"type":"text","text":"@Aspect"}]},{"type":"text","text":"註解中指定"},{"type":"codeinline","content":[{"type":"text","text":"perthis"}]},{"type":"text","text":"來聲明"},{"type":"codeinline","content":[{"type":"text","text":"perthis"}]},{"type":"text","text":"切面。考慮以下示例:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Aspect(\"perthis(com.xyz.myapp.SystemArchitecture.businessService())\")\npublic class MyAspect {\n\n private int someState;\n\n @Before(com.xyz.myapp.SystemArchitecture.businessService())\n public void recordServiceUsage() {\n // ...\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在前面的示例中,“ "},{"type":"codeinline","content":[{"type":"text","text":"perthis"}]},{"type":"text","text":"”子句的作用是爲每個執行業務服務的唯一服務對象(每個與切入點表達式匹配的連接點綁定到“ "},{"type":"codeinline","content":[{"type":"text","text":"this"}]},{"type":"text","text":"”的唯一對象)創建一個切面實例。切面實例是在服務對象上首次調用方法時創建的。當服務對象超出範圍時,切面將超出範圍。在創建切面實例之前,其中的任何通知都不會執行。一旦創建了切面實例,在其中聲明的通知就會在匹配的連接點上執行,但僅當服務對象與此切面相關聯時才執行。有關每個子句的更多信息,請參見AspectJ編程指南。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"pertarget"}]},{"type":"text","text":"實例化模型的工作方式與"},{"type":"codeinline","content":[{"type":"text","text":"perthis"}]},{"type":"text","text":"完全相同,但是它在匹配的連接點爲每個唯一目標對象創建一個切面實例。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"5.4.7 AOP例子"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在你已經瞭解了所有組成部分是如何工作的,我們可以將它們組合在一起做一些有用的事情。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有時由於併發問題(例如,死鎖失敗),業務服務的執行可能會失敗。如果重試該操作,則很可能在下一次嘗試中成功。對於適合在這種情況下重試的業務服務(不需要爲解決衝突而需要返回給用戶的冪等操作),我們希望透明地重試該操作,以避免客戶端看到"},{"type":"codeinline","content":[{"type":"text","text":"PessimisticLockingFailureException"}]},{"type":"text","text":"。這是一個明顯跨越服務層中的多個服務的需求,因此非常適合通過切面實現。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲我們想重試該操作,所以我們需要使用環繞通知,以便可以多次調用"},{"type":"codeinline","content":[{"type":"text","text":"proceed"}]},{"type":"text","text":"。以下清單顯示了基本切面的實現:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Aspect\npublic class ConcurrentOperationExecutor implements Ordered {\n\n private static final int DEFAULT_MAX_RETRIES = 2;\n\n private int maxRetries = DEFAULT_MAX_RETRIES;\n private int order = 1;\n\n public void setMaxRetries(int maxRetries) {\n this.maxRetries = maxRetries;\n }\n\n public int getOrder() {\n return this.order;\n }\n\n public void setOrder(int order) {\n this.order = order;\n }\n\n @Around(\"com.xyz.myapp.SystemArchitecture.businessService()\")\n public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {\n int numAttempts = 0;\n PessimisticLockingFailureException lockFailureException;\n do {\n numAttempts++;\n try {\n return pjp.proceed();\n }\n catch(PessimisticLockingFailureException ex) {\n lockFailureException = ex;\n }\n } while(numAttempts <= this.maxRetries);\n throw lockFailureException;\n }\n\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請注意,切面實現了"},{"type":"codeinline","content":[{"type":"text","text":"Ordered"}]},{"type":"text","text":"接口,以便我們可以將切面的優先級設置爲高於事務通知的優先級(每次重試時都需要一個新的事務)。"},{"type":"codeinline","content":[{"type":"text","text":"maxRetries"}]},{"type":"text","text":"和"},{"type":"codeinline","content":[{"type":"text","text":"order"}]},{"type":"text","text":"屬性均由Spring配置。通知的主要動作發生在"},{"type":"codeinline","content":[{"type":"text","text":"doConcurrentOperation"}]},{"type":"text","text":"中。請注意,目前,我們將重試邏輯應用於每個"},{"type":"codeinline","content":[{"type":"text","text":"businessService()"}]},{"type":"text","text":"。我們嘗試繼續,如果失敗並出現"},{"type":"codeinline","content":[{"type":"text","text":"PessimisticLockingFailureException"}]},{"type":"text","text":",則我們將再次重試,除非我們用盡了所有重試嘗試。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對應的Spring配置如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"html"},"content":[{"type":"text","text":"\n\n\n \n \n\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了完善切面,使其僅重試冪等操作,我們可以定義以下冪等註解:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Retention(RetentionPolicy.RUNTIME)\npublic @interface Idempotent {\n // marker annotation\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,我們可以使用註解來註釋服務操作的實現。切面更改爲僅重試冪等操作涉及更改切入點表達式,以便僅"},{"type":"codeinline","content":[{"type":"text","text":"@Idempotent"}]},{"type":"text","text":"操作匹配,如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"@Around(\"com.xyz.myapp.SystemArchitecture.businessService() && \" +\n \"@annotation(com.xyz.myapp.service.Idempotent)\")\npublic Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {\n // ...\n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"作者"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"個人從事金融行業,就職過易極付、思建科技、某網約車平臺等重慶一流技術團隊,目前就職於某銀行負責統一支付系統建設。自身對金融行業有強烈的愛好。同時也實踐大數據、數據存儲、自動化集成和部署、分佈式微服務、響應式編程、人工智能等領域。同時也熱衷於技術分享創立公衆號和博客站點對知識體系進行分享。關注公衆號:"},{"type":"text","marks":[{"type":"strong"}],"text":"青年IT男"},{"type":"text","text":" 獲取最新技術文章推送!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"博客地址:"},{"type":"text","text":" "},{"type":"link","attrs":{"href":"http://youngitman.tech/","title":null},"content":[{"type":"text","text":"http://youngitman.tech"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"CSDN:"},{"type":"text","text":" "},{"type":"link","attrs":{"href":"https://blog.csdn.net/liyong1028826685","title":null},"content":[{"type":"text","text":"https://blog.csdn.net/liyong1028826685"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"微信公衆號:"},{"type":"text","text":" "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0e/0e1ef0e34fb3ecf6b6c553250015a224.jpeg","alt":null,"title":"","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章