1. 程式人生 > >深入理解Spring AOP 1.0

深入理解Spring AOP 1.0

#### 本文相關程式碼(來自[官方原始碼](https://github.com/spring-projects/spring-framework.git "官方原始碼")spring-test模組)請參見[spring-demysify](https://github.com/whalefall541/spring-demysify) org.springframework.mylearntest包下。 ### 統稱能夠實現AOP的語言為AOL,即(Aspect-Oriented Language),其他Aspectj - AspectC - AspectC++ - Aspect.Net - AspectL(Lisp) - AspectPHP - ...... JAVA中AOP實現方式 1. 動態代理 - 在執行期間,為相應的介面動態生成對應的代理物件,將橫切關注點邏輯封裝到動態代理的InvocationHandler中,然後在系統執行期間,根據橫切關注點需要織入的模組位置,將橫切邏輯織入到相應的代理類中。 2. 動態位元組碼增強 - 使用ASM或者CGLIB等Java工具庫,在程式執行期間,動態構建位元組碼的class檔案。 - 在執行期間通過動態位元組碼增強技術織入橫切邏輯,為這些系統模組類生成相應的子類,而將橫切邏輯加到這些子類中,讓應用程式的執行期間使用的是這些動態生成的子類,從而達到將橫切邏輯織入系統的目的。 - 如果需要擴充套件的類以及類中的例項方法等宣告為final的話,則無法對其進行子類化的擴充套件。Spring AOP在無法使用動態代理機制進行AOP功能的擴充套件的時候,會使用CGLIB庫的動態位元組碼增強技術來實現AOP的擴充套件。 3. java程式碼生成 - EJB容器根據部署描述符檔案提供了織入資訊,會為相應的功能模組類根據描述符所提供的資訊生成對應的java程式碼,然後通過部署工具或者部署介面編譯java程式碼生成對應的java類。之後部署到EJB容器的功能模組類就可以正常工作了。 4. 自定義類載入器 - 所有的java程式的class都要通過相應的類載入器(Classloader)載入到Java虛擬機器之後才可以執行。預設的類載入器會讀取class位元組碼檔案,然後按照class位元組碼規範,解析並載入這些class檔案到虛擬機器執行。如果我能夠在這個class載入到虛擬機器執行期間,將橫切邏輯織入到class檔案的話,是不是就完成了AOP和OPP的融合呢? - 我們可以通過自定義類載入器的方式完成橫切邏輯到系統的織入,自定義類載入器通過讀取外部檔案規定的織入規則和必要資訊,在載入class檔案期間就可以將橫切邏輯新增到系統模組類的現有邏輯中,然後將改動後的class交給java虛擬機器執行。通過類載入器,我們基本可以對大部分類以及相應的例項進行織入,功能於之前的幾種方式相比當然強大很多。不過這種方式最大的問題就是類載入器本身的使用。某些應用伺服器會控制整個的類載入體系,所以,在這樣的場景下會造成一定的問題。 - Jboss AOP 和已經併入AspectJ專案的AspectWerkz框架都是採用自定義類載入器的方式實現。 5. AOL擴充套件 - AOL擴充套件是最強大、也是最難掌握的一種方式,我們之前提到AspectJ就屬於這種方式。在這種方式中,AOP的各種概念在AOL中大都有一一對應的實體。我們可以使用擴充套件過的AOL,實現任何AOP概念實體甚至OPP概念實體,比如Aspect以及Class。所有的AOP概念在AOL中得到了最完美的表達。 - 採用擴充套件的AOL,在AOP概念的表達上頗具例項,使得AOP涉及的所有橫切關注點邏輯在進行織入之前,可以自由自在地存活在自己的“國度中”。而像“編譯到靜態類可以提升系統執行效能”,“java虛擬機器可以像載入平常類那種,載入已經織入相應邏輯的AO元件所在的檔案並執行”等特點。使用這種方式,需要學習一門擴充套件的AOL語言。 ![](https://oscimg.oschina.net/oscnet/up-8b44413c3499ab8c5aaf6884bdd28946b6d.png) 一些單詞的含義: - Joinpoint 切點 - Pointcut 切點表示式: - 直接指定Joinpoint所在的方法名稱 - 正則表示式:Jboss、Spring AOP、AspectWerkz等均支援 - 使用特定的Pointcut表達語言:Spring 2.0以後,藉助於AspectJ的Pointcut表述語言直譯器,支援使用AspectJ的Pointcut表述語言來指定Pointcut。 ![](https://oscimg.oschina.net/oscnet/up-1b5e31d34b8e44dfa4475d758149d2a363c.png) - Advice 切面 - 1. Before Advice - 2. After Advice - After returning - After throwing - After Advice(finally) - 3. After Around ![](https://oscimg.oschina.net/oscnet/up-6dde3692e0b345a926714217b05fc1844f1.png) - 4. Introduction - 在AspectJ中稱Inter-Type Declaration,在JBoss AOP 中稱Mix-in,都是指這同一種類型的Advice。與之前的幾種Advice型別不同,Introduction不是根據橫切邏輯在Joinpoint處的執行時機來區分的,而是根據它可以完成的功能而區別於其他Advice型別。 - AspectJ採用靜態織入的形式,那麼物件在使用的時候,Itroduction邏輯已經是編譯織入完成的。所以理論上來說,AspectJ提供的Introduction型別的Advice,在現有Java平臺上的AOP實現中是效能最好的;而像JBosss AOP或者Spring AOP等採用動態織入的AOP實現,Introduction的效能要稍遜一籌。 ### Aspect Aspect是對系統中的橫切關注點邏輯進行模組化封裝的AOP的概念實體。通常情況下,Aspect可以包含多個Pointcut以及相關Advice定義。 ![](https://oscimg.oschina.net/oscnet/up-54403168dcfb0ec2d3084e5f49d0e678cc1.png) ### 設計模式之代理模式 1. 靜態代理 ```java package org.springframework.mylearntest.aop.staticproxy; public interface IRequestable { void request(); } ``` ```java package org.springframework.mylearntest.aop.staticproxy; public class RequestableImpl implements IRequestable{ @Override public void request() { System.out.println(" request process in RequestableImpl"); } } ``` ```java package org.springframework.mylearntest.aop.staticproxy; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ServiceControlRequestableProxy implements IRequestable{ private static final Logger logger = LoggerFactory.getLogger(ServiceControlRequestableProxy.class); private IRequestable requestable; public ServiceControlRequestableProxy(IRequestable target) { this.requestable = target; } @Override public void request() { System.out.println("request process in ServiceControlRequestableProxy"); requestable.request(); } public static void main(String[] args) { IRequestable target = new RequestableImpl();// 需要被代理的物件 IRequestable proxy = new ServiceControlRequestableProxy(target); // 以構造方法形式將被代理物件傳入代理者中 proxy.request();// 讓代理者去處理請求 } } ``` 2. 動態代理 - 動態代理機制主要由一個類和一個介面組成,即java.lang.reflect.Proxy類和java.lang.reflect.InvocationHadler介面。 ```java package org.springframework.mylearntest.aop.dynamicproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class RequestCtrlInvocationHandler implements InvocationHandler { private Object target; public RequestCtrlInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("reflect invoke before target method"); if ("request".equals(method.getName())) { System.out.println("dynamic proxy target method"); return method.invoke(target, args); } return null; } } ``` ```java package org.springframework.mylearntest.aop.dynamicproxy; import org.springframework.mylearntest.aop.staticproxy.IRequestable; import org.springframework.mylearntest.aop.staticproxy.RequestableImpl; import java.lang.reflect.Proxy; @SuppressWarnings("rawtypes") public class Test4DynamicProxy { public static void main(String[] args) { // arg1:類載入器 arg2:介面資訊 arg3:實現InvocationHandler的類 並傳入需要代理的物件 IRequestable requestable = (IRequestable) Proxy.newProxyInstance( Test4DynamicProxy.class.getClassLoader(), new Class[]{IRequestable.class}, new RequestCtrlInvocationHandler(new RequestableImpl())); requestable.request(); } } ``` 如果想深入瞭解動態代理,請閱讀《java reflect in action》。 3. CGLIB位元組碼生成 - 需要使用CGLIB擴充套件子類,首先需要實現一個net.sf.cglib.proxy.Callback,不過更多的時候,我們會直接使用net.sf.cglib.proxy.MethodInterceptor介面(MethodInterceptor擴充套件了Callback介面)。 ```java package org.springframework.mylearntest.aop.CGLIBClassGenerate; public class Requestable { public void request(){ System.out.println("req in requestable without implement any interface"); } } ``` ```java package org.springframework.mylearntest.aop.CGLIBClassGenerate; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class RequestCtrlCallback implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { if (method.getName().equals("request")) { System.out.println("proxy generated by cglib intercept method request"); return methodProxy.invokeSuper(o, objects); } return null; } } ``` ```java package org.springframework.mylearntest.aop.CGLIBClassGenerate; import org.springframework.cglib.proxy.Enhancer; public class Test4CGLIB { public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Requestable.class); enhancer.setCallback(new RequestCtrlCallback()); Requestable proxy = (Requestable) enhancer.create(); proxy.request(); } } ``` ### AOP中的Pointcut 如果Pointcut型別為TruePointcut,預設會對系統中的所有物件,以及物件上所有被支援的Joinpoint進行匹配。 ```java package org.springframework.aop; springframework.aop.support.MethodMatchers public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); Pointcut TRUE = TruePointcut.INSTANCE; } ``` ```java package org.springframework.aop; import java.io.Serializable; @SuppressWarnings("serial") final class TruePointcut implements Pointcut, Serializable { public static final TruePointcut INSTANCE = new TruePointcut(); private TruePointcut() { } @Override public ClassFilter getClassFilter() { return ClassFilter.TRUE; } @Override public MethodMatcher getMethodMatcher() { return MethodMatcher.TRUE; } private Object readResolve() { return INSTANCE; } @Override public String toString() { return "Pointcut.TRUE"; } } ``` ClassFilter和MethodMatcher分別用於匹配將被執行織入操作的物件以及相應的方法。之所以將型別匹配和方法匹配分開定義,是因為可以重用不同級別的匹配定義,並且可以在不同級別或者相同級別上進行組合操作,或者強制讓某個子類只覆蓋(Override)相應方法定義等。 ```java package org.springframework.aop; @FunctionalInterface public interface ClassFilter { boolean matches(Class clazz); ClassFilter TRUE = TrueClassFilter.INSTANCE; } ```java package org.springframework.aop; import java.lang.reflect.Method; public interface MethodMatcher { boolean matches(Method method, Class targetClass); boolean isRuntime(); boolean matches(Method method, Class targetClass, Object... args); MethodMatcher TRUE = TrueMethodMatcher.INSTANCE; } ``` - 當isRuntime返回false時,表示不會考慮具體Joinpoint的方法引數,這種型別的MethodMatcher稱之為staticMethodMatcher。因為不用每次都檢查引數,那麼對於同樣型別的方法匹配結果,就可以在框架內部快取以提高效能。 - 當isRuntime返回true時,表明MethodMatcher將會每次都對方法呼叫的引數進行匹配檢查,這種型別的MethodMatcher稱之為DynamicMethodMatcher。因為每次都要對方法引數進行檢查,無法對匹配的結果進行快取,所以,匹配效率相對於StaticMethodMatcher來說要差。而且大部門情況下,staticMethodMatcher已經可以滿足需要。最好避免使用DynamicMethodMatcher型別。 - 如果boolean matches(Method method, Class targetClass);返回true時,三個引數的matches將會被執行,以進一步檢查匹配條件;如果boolean matches(Method method, Class targetClass);返回false,那麼不管這個MethodMatcher是staticMethodMatcher還是DynamicMethodMatcher,該結果已經是最終結果,三個引數的方法肯定不會被執行了。 ![](https://oscimg.oschina.net/oscnet/up-56c82a867727a95b1dad667aae23f35016f.png) ![常見pointcut](https://oscimg.oschina.net/oscnet/up-e2d8234131d2a7bcda1159c35d298a64d3e.png "常見pointcut") #### 分述各種Pointcut 1. NameMatchMethodPointcut - 最簡單的Pointcut實現,屬於StaticMethodMatcherPointcut的子類,可以根據自身指定一組方法名稱與Joinpoint處的方法的方法名稱進行匹配。 ```java NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut(); pointcut.setMappedName("matches"); // 或者傳入多個方法名 pointcut.setMappedNames(new String[]{"matches", "isRuntime"}); // 簡單模糊匹配 pointcut.setMappedNames(new String[]{"match*", "matches", "mat*es" }); ``` - 此方法無法對過載的方法名進行匹配,因為它僅對方法名進行匹配,不會考慮引數相關資訊,而且也沒有提供可以指定引數匹配資訊的途徑。 2. JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut - StaticMethodMatcherPointcut的子類有一個專門提供基於正則表示式的實現分支,以抽象類AbstractRegexpMethodPointcut為統帥,聲明瞭pattern 和 patterns屬性,可以指定一個或者和多個正則表示式的匹配模式。其下設JdkRegexpMethodPointcut和Perl5RegexpMethodPointcut兩種具體實現。JdkRegexpMethodPointcut是在JDK 1.4之後引入JDK標準正則表示式。 ```java JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut(); pointcut.setPattern(".*match.*"); pointcut.setPatterns(new String[]{".*match.", ".*matches"}); ``` - 注意正則表示式匹配模式必須匹配整個方法簽名(Method signature)的形式指定,而不能像NameMatchMethodPointcut那樣僅給出匹配的方法名稱。 - Perl5RegexpMethodPointcut實現使用jakarta ORO提供正則表示式支援, 1. 可以通過pattern或者patterns物件屬性指定一個或者多個正則表示式 2. 指定正則表示式匹配模式應該覆蓋匹配整個方法簽名,而不是隻指定到方法名稱部分。 3. AnnotationMatchingPointcut ```java package org.springframework.mylearntest.aop.annotationmatchingpointcut; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ClassLevelAnnotation { } ``` ```java package org.springframework.mylearntest.aop.annotationmatchingpointcut; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MethodLevelAnnotation { } ``` ```java package org.springframework.mylearntest.aop.annotationmatchingpointcut; @ClassLevelAnnotation public class GenericTargetObject { @MethodLevelAnnotation public void getMethod1() { System.out.println("getMethod1"); } public void getMethod2() { System.out.println("getMethod2"); } } ``` ```java AnnotationMatchingPointcut pointcut = new AnnotationMatchingPointcut(ClassLevelAnnotation.class); // 也可以通過靜態方法 AnnotationMatchingPointcut pointcut1 = AnnotationMatchingPointcut.forClassAnnotation(MethodLevelAnnotation.class); // 同時限定 AnnotationMatchingPointcut pointcut2 = AnnotationMatchingPointcut.forClassAnnotation(ClassLevelAnnotation.class); ``` 4. ComposablePointcut Spring AOP提供Pointcut邏輯運算的Pointcut實現。它可以進行Pointcut之間的“並”以及“交”運算。 ```java package org.springframework.mylearntest.aop.pointcut.composablePointcut; import org.junit.Assert; import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.Pointcuts; public class Test4ComposablePointcut { public static void main(String[] args) { ComposablePointcut pointcut1 = new ComposablePointcut(new ClassFilter() { @Override public boolean matches(Class clazz) { return false; } }, MethodMatcher.TRUE); ComposablePointcut pointcut2 = new ComposablePointcut(new ClassFilter() { @Override public boolean matches(Class clazz) { return false; } }, MethodMatcher.TRUE); // union intersection ComposablePointcut union = pointcut1.union(pointcut2); ComposablePointcut intersection = pointcut1.intersection(union); Assert.assertEquals(pointcut1,intersection); // combine classFilter with methodMatcher pointcut2.union(new ClassFilter() { @Override public boolean matches(Class clazz) { return false; } }).intersection(MethodMatcher.TRUE); // just compute between pointcut, use org.springframework.aop.support.Pointcuts Pointcut pointcut3 = new Pointcut() { @Override public ClassFilter getClassFilter() { return null; } @Override public MethodMatcher getMethodMatcher() { return null; } }; Pointcut pointcut4 = new Pointcut() { @Override public ClassFilter getClassFilter() { return null; } @Override public MethodMatcher getMethodMatcher() { return null; } }; Pointcut union1 = Pointcuts.union(pointcut3, pointcut4); Pointcut intersection1 = Pointcuts.intersection(pointcut3, pointcut4); } } ``` 5. ControlFlowPointcut ControlFlowPointcut匹配程式的呼叫流程,不是對某個方法執行所在Joinpoint處的單一特徵進行匹配,而是要被特定的類執行時,才會進行方法攔截。 因為ControlFlowPointcut型別的Pointcut 需要在執行期間檢查程式的呼叫棧,而且每次方法呼叫都需要檢查,所以效能比較差。 ### Spring Aop中的Advice Spring 中各種Advice 和 Aop Alliance標準介面之間的關係。 ![](https://oscimg.oschina.net/oscnet/up-22bd58e5adaa1b4d81cedc603cd6a3a4675.png) - 在Spring中,Advice按照其自身例項能否在目標物件類的所有例項中共享這一標準,可以劃分為兩大類,即per-calss型別的Advice 和 per-instance型別的Advice。 #### per-class per-class的Advice是指,該型別的Advice的例項可以在目標物件類的所有例項之間共享。這種型別的Advice通常只是提供方法的攔截功能,不會對目標物件類儲存任何狀態或者新增新的特性。 1. BeforeAdvice ```java package org.springframework.mylearntest.aop.advice; import org.apache.commons.io.FileUtils; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.core.io.Resource; import java.lang.reflect.Method; public class ResourceSetupBeforeAdvice implements MethodBeforeAdvice { private Resource resource; public ResourceSetupBeforeAdvice(Resource resource) { this.resource = resource; } @Override public void before(Method method, Object[] args, Object target) throws Throwable { if (!resource.exists()) { FileUtils.forceMkdir(resource.getFile()); } } } ``` 2. ThrowsAdvice ```java package org.springframework.mylearntest.aop.advice; import org.omg.CORBA.portable.ApplicationException; import org.springframework.aop.ThrowsAdvice; import java.lang.reflect.Method; public class ExceptionBarrierThrowsAdvice implements ThrowsAdvice { public void afterThrowing(Throwable t) { // 普通異常處理 } public void afterThrowing(RuntimeException t) { // 執行時異常處理 } public void afterThrowing(Method m, Object[] args, Object target, ApplicationException e) { // 處理應用程式生成的異常 } } ``` 3. AfterReturningAdvice 此Advice可以訪問到當前Joinpoint的方法返回值、方法、方法引數以及所在的目標物件,但是不能更改返回值,可以使用Around Advice來更改返回值。 4. Around Advice Spring中沒有定義Around Advice ,而是直接使用AOP Alliance的標準介面,實現 MethodInterceptor即可。 #### per-instance per-instance型別的Advice不會在目標類所有物件例項之間共享,而是會為不同的例項物件儲存它們各自的狀態以及相關邏輯。在Spring中Introduction就是唯一的一種per-instance型Advice。 - Introduction 可以在不改動目標類定義的情況下,為目標類新增新的屬性以及行為。 - 在Spring中,為目標物件新增新的屬性和行為必須宣告相應的介面以及相應的實現。這樣,再通過特定的攔截器將新的介面定義以及實現類中的邏輯附加到目標物件之上。之後,目標物件就擁有了新的狀態和行為。這個特定的攔截器是org.springframework.aop.IntroductionInterceptor。 - Introduction繼承了MethodInterceptor以及DynamicIntroductionAdvice,通過DynamicIntroductionAdvice,我們可以界定當前的IntroductionInterceptor為哪些介面類提供相應的攔截功能。通過MethodInterceptor,IntroductionInterceptor就可以處理新新增的介面上的方法呼叫了。通常情況下,對於IntroductionInterceptor來說,如果是新增加的介面上的方法呼叫,不必去呼叫MethodInterceptor的proceed()方法。當前被攔截的方法實際上是整個呼叫鏈中要最終執行的唯一方法。 ![Introduction相關類圖](https://oscimg.oschina.net/oscnet/up-dab617d645b217c3618bf51c07bdfdca5d8.png "Introduction相關類圖") ##### DelegatingIntroductionInterceptor DelegatingIntroductionInterceptor不會自己實現將要新增到目標物件上的新邏輯行為,而是委派給其他的實現類。 - 使用DelegatingIntroductionInterceptor增強Developer。介面省略。 ```java package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public class Developer implements IDeveloper{ @Override public void developSoftware() { System.out.println(" do some developing ..."); } } ``` 1. 為新的狀態和行為定義介面。我們要為實現類新增增強的功能,首先需要將需求的職能以介面定義的形式宣告。 ```java package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public interface ITester { boolean isBusyAsTester(); void testSoftware(); } ``` 2. 給出新的介面的實現類。介面實現類給出將要新增到目標物件的具體邏輯。當目標物件要行使新的職能的時候,會通過該實現類尋求幫忙。 ```java package org.springframework.mylearntest.aop.advice.perinstance.delegatingIntroductionInterceptor; public class Tester implements ITester{ private boolean busyAsTester; public void setBusyAsTester(boolean busyAsTester) { this.busyAsTester = busyAsTester; } @Override public boolean isBusyAsTester() { return busyAsTester; } @Override public void testSoftware() { System.out.println("do some developing and test ..."); } } ``` 3. 通過DelegatingIntroductionInterceptor進行Introduction攔截。有了新增加的職能的介面以及相應的實現類,使用DelegatingIntroductionInterceptor,我們可以把具體的Introduction攔截委託給具體的實現類來完成。 ```java ITester delegator = new Tester(); DelegatingIntroductionInterceptor interceptor = new DelegatingIntroductionInterceptor(delegator); // 進行織入 ITester tester = (ITester)weaver.weave(developer).with(interceptor).getProxy(); tester.testSoftware(); ``` 4. 雖然,DelegatingIntroductionInterceptor是Introduction型Advice的一個實現,但它的實現根本就有兌現Introduction作為per-instance型的承諾。實際上DelegatingIntroductionInterceptor會使用它所持有的同一個"delegate" 介面例項,供同一目標類的所有例項共享使用。如果要想嚴格達到Introduction型Advice的效果,我們應該使用DelegatePerTargetObjectIntroductionInterceptor。 ##### DelegatePerTargetObjectIntroductionInterceptor 與DelegatingIntroductionInterceptor不同,DelegatePerTargetObjectIntroductionInterceptor會在內部持有一個目標物件與相應Introduction邏輯實現類之間的對映關係。當每個物件上的新定義的介面方法被呼叫的時候,DelegatePerTargetObjectIntroductionInterceptor會攔截這些呼叫,然後以目標物件例項作為鍵,到它持有的那個對映關係中取得對應當前目標物件例項的Introduction實現例項。 ```java DelegatePerTargetObjectIntroductionInterceptor interceptor1 = new DelegatePerTargetObjectIntroductionInterceptor(Tester.class,ITester.class); ``` - 擴充套件DelegatingIntroductionInterceptor ```java package org.springframework.mylearntest.aop.advice.perinstance; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.lang3.StringUtils; import org.springframework.aop.support.DelegatingIntroductionInterceptor; public class TesterFeatureIntroductionInterceptor extends DelegatingIntroductionInterceptor implements ITester { public static final long serialVersionUID = -3387097489523045796L; private boolean busyAsTester; @Override public Object invoke(MethodInvocation mi) throws Throwable { if (isBusyAsTester() && StringUtils.contains(mi.getMethod().getName(), "developSoftware")) { throw new RuntimeException("I'am so tired"); } return super.invoke(mi); } @Override public boolean isBusyAsTester() { return busyAsTester; } public void setBusyAsTester(boolean busyAsTester) { this.busyAsTester = busyAsTester; } @Override public void testSoftware() { System.out.println("I will ensure the quality"); } } ``` #### Spring AOP 中的Aspect - Advisor代表Spring中的Aspect,但是與正常的Aspect不同,Advisor通常只持有一個Pointcut和一個Advice。而理論上,Aspect定義中可以有多個Pointcut和多個Advice,所以Advisor是一種特殊的Aspect。 ##### PointcutAdvisor ![](https://oscimg.oschina.net/oscnet/up-a07fb97fa23007ab71c566724aef6d68b6f.png) ![](https://oscimg.oschina.net/oscnet/up-58fd138fecf5bb12603ff296eb439e0360c.png) - 實際上,org.springframework.aop.PointcutAdvisor才是真正定義的有一個Pointcut和一個Advice的Advisor,大部分的Advisor實現全部是在PointcutAdvisor下的。 1. DefaultPointcutAdvisor