本文已收錄 【修煉內功】躍遷之路
在以前的文章中介紹了Spring的IoC( Resource | BeanDefinitionReader | BeanFactory | ApplicationContext),若是說前者是Spring的基石,則本篇要介紹的AOP則能夠稱做是Spring的點睛之筆,它在整個Spring生態中扮演着重要的角色
Aspect-oriented Programming (AOP) complements Object-oriented Programming (OOP) by providing another way of thinking about program structure. The key unit of modularity in OOP is the class, whereas in AOP the unit of modularity is the aspect. Aspects enable the modularization of concerns that cut across multiple types and objects.
代理能夠看做是對調用目標的一個包裝,目標代碼的調用不是直接發生的而是經過代理完成,以此來讓調用者與實現者之間解耦,從而(在無侵入的狀況下)在目標代碼以外實現一些額外(加強)功能html
實現代理的方法有不少種java
維基百科-面向切面的程序設計Aspect-Oriented Programming,面向切面的程序設計,是計算機科學中的一種程序設計思想,旨在將橫切關注點與業務主體進行進一步分離,以提升程序代碼的模塊化程度。經過在現有代碼基礎上增長額外的通知(Advice)機制,可以對被聲明爲「切點(Pointcut)」的代碼塊進行統一管理與裝飾。git
從AOP的定義上來看,其與代理模式十分類似,那能夠說AOP就是代理模式的一種實現麼?並不是如此,AOP相比於代理模式表現的更爲靈活也更細膩,典型的框架如 AspectJgithub
Spring AOP 將面向切面的設計思想引入到了Spring IoC中,但Spring AOP並非AOP的完備實現,其設計目標在於:spring
Spring AOP的做用範圍僅限於Spring IoC中,更爲通俗的講,Spring AOP的切面只能做用在Spring IoC所管理的Bean中express
Rather, the aim is to provide a close integration between AOP implementation and Spring IoC, to help solve common problems in enterprise applications.
Spring AOP僅支持方法級的切面,其更像是給目標(切面)方法定義了一個攔截器(method-interceptor)apache
Spring AOP currently supports only method execution join points (advising the execution of methods on Spring beans).
Spring AOP具體的能力及設計目標見 Spring AOP Capabilities And Goals編程
Spring AOP藉助了AspectJ的不少能力(註解、切面匹配、攔截器、等),但其織入(Weaving)過程並未使用AspectJ,而是使用了 JDK Proxy/CGLIB 建立代理類,經過反射的方式實現目標方法的調用,Spring AOP及AspectJ在功能上的區別比較見 Comparing Spring AOP and AspectJsegmentfault
Spring框架並未直接引入CGLIB,而是本身維護(repackaging)了一套源碼(包括asm在內),可參考 spring-core內的 org.springframework.cglib包及 org.springframework.asm包
相比於AspectJ之類的AOP框架而言,Spring AOP更加簡潔,但Spring AOP是如何實現的?這裏不妨先留幾個疑問,帶着問題去理解Spring AOP的實現邏輯緩存
在深刻Spring AOP的實現原理以前,先熟悉幾個AOP的基本概念 (AOP Concepts)
Pointcut
切入點,在哪些類的哪些方法上進行切入(攔截),其一般表現爲一個表達式(AspectJExpressionPointcut),經過該表達式匹配哪些類(ClassFilter)及哪些方法(MethodMatcher)須要被攔截代理
JoinPoint
鏈接點,因爲Spring AOP僅支持方法,在Spring AOP中能夠理解爲,經過Pointcut匹配到的須要被攔截的方法
Advice
通知,攔截到鏈接點後,須要代理執行的代碼邏輯,其一共分爲 Around、Before、After、AfterReturning、AfterThrowing 五類
Aspect
切面,即 Pointcut + Advice,在哪些類的哪些方法上,代理執行哪些邏輯
Weaving
織入,將切面應用到目標對象,生成代理對象的過程
Target Object
目標(被代理)類對象,鏈接點所在的類對象
Proxy Object
生成的代理對象
Introduction
引入,在不修改目標類代碼的前提下,可在運行期動態的增長一些方法(或屬性)
Spring AOP的註冊有多種方式,主要以註解及xml配置爲主,本篇着重介紹註解方式,xml配置的方式原理相通
爲了啓用Spring AOP,通常都會使用註解@EnableAspectJAutoProxy,或者xml配置<aop:aspectj-autoproxy>
@Import(AspectJAutoProxyRegistrar.class) public @interface EnableAspectJAutoProxy { boolean proxyTargetClass() default false; boolean exposeProxy() default false; }
proxyTargetClass及exposeProxy參數的做用參見JavaBean Properties,重點在AspectJAutoProxyRegistrar(@Import註解的使用在之後的章節中介紹)實現了ImportBeanDefinitionRegistrar接口(其做用在於動態註冊Bean)
跟蹤源碼(AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary)會發現@EnableAspectJAutoProxy註解經過AspectJAutoProxyRegistrar註冊器註冊了AnnotationAwareAspectJAutoProxyCreator,該Creator實現了BeanPostProcessor
Bean是如何被建立的一文中介紹過,在Bean實例化並初始化完成後,會調用BeanPostProcessor#postProcessAfterInitialization,該方法能夠返回一個新的實例對象,AnnotationAwareAspectJAutoProxyCreator對目標對象的代理建立過程,可能就發生在此步驟
// org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization @Override public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) { if (bean != null) { // ... 略去 // 切面匹配,若是須要則生成代理bean return wrapIfNecessary(bean, beanName, cacheKey); } return bean; }
看到wrapIfNecessary
是否有豁然開朗的感受?
因此,Spring AOP對目標對象的代理建立過程發生在BeanPostProcessor#postProcessAfterInitialization階段,即目標Bean實例化且初始化完成以後
AnnotationAwareAspectJAutoProxyCreator實際直接實現了 InstantiationAwareBeanPostProcessor接口,在目標對象建立以前便會有一個短路的操做,可能直接在該步驟生成代理對象而不須要目標對象的實例化,具體邏輯能夠參考 AbstractAutoProxyCreator#postProcessBeforeInstantiation
查看wrapIfNecessary的邏輯,總體上並不複雜,找到該bean全部匹配的切面Advisors,並根據所匹配的切面建立代理bean
首選關心的是,如何找到全部匹配的Advisors
AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean
AbstractAdvisorAutoProxyCreator#findEligibleAdvisors
AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors
Advisor的註冊有兩大途徑,直接註冊Advisor類型的Bean 或者 註冊被@Aspect修飾的Bean
AbstractAdvisorAutoProxyCreator#findCandidateAdvisors
BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans
這一步驟其實並不複雜,在BeanFactory中找到全部註冊爲Advisor類型的Bean並提早初始化
查看Advisor的源碼十分簡單,只有一個方法會比較感興趣
public interface Advisor { Advice getAdvice(); }
而Advice則顯得更加神祕,沒有任何方法接口定義
public interface Advice {}
留個懸念,客官且看
Q: Advisor是什麼?Advice的實現類都有哪些?如何直接註冊Advisor的Bean?經過註冊Advisor Bean來實現AOP的場景有哪些?
BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
Spring將全部註冊的bean-names拿出遍歷,過濾出被@Aspect註解修飾的Bean,經過@Apsect註解及Aspect-Bean信息生成BeanFactoryAspectInstanceFactory,用於後續Advisor的解析(ReflectiveAspectJAdvisorFactory#getAdvisors)
從流程圖能夠看出,Advisor的解析主要來自方法及屬性兩部分,方法主要用來解析@Around、@Before、@After、@AfterReturing、@AfterThrowing,屬性主要用來解析@DeclareParents
ReflectiveAspectJAdvisorFactory#getAdvisor
Spring會查找被@Around、@Before、@After、@AfterReturing、@AfterThrowing(若是一個方法同時被多個註解修飾,按順序只會匹配第一個被找到的註解)修飾的方法,將註解的信息封裝爲AspectJExpressionPointcut,將方法的內部邏輯封裝爲不一樣類型的xxxAdvice,並最終將上述的Pointcut及Advice組合封裝爲InstantiationModelAwarePointcutAdvisorImpl提供出去
這裏出現了開篇提到的幾個概念 Pointcut、Advice
Pointcut
Pointcut的實現很是多,深刻以前先瀏覽下Pointcut的接口定義
public interface Pointcut { ClassFilter getClassFilter(); MethodMatcher getMethodMatcher(); }
不難猜測,Spring AOP就是經過這兩個方法來判斷哪些類的哪些方法須要被代理,反過來說經過這兩個方法判斷當前Bean的各個方法有沒有以及匹配上了哪些Advice
衆多的Pointcut實現中,重點介紹其中的幾個
該Pointcut的實現很是簡單,不管入參是什麼,ClassFilter及MethodMatcher的matches方法總會返回true
該Pointcut只關注目標類上的註解修飾,ClassFilter#matches用來判斷目標類上是否存在指定的註解,MethodMatcher#matches總會返回true
AspectJExpressionPointcut
該Pointcut是本篇的重點,一般而言不管使用註解方式仍是xml方式配置AOP,一般在聲明Pointcut的時候都會定義一段表達式用來匹配咱們想要攔截/代理的方法,這樣的聲明方式Spring AOP均會將其封裝爲AspectJExpressionPointcut,其內部的匹配邏輯比較複雜,在這裏很少展開討論,感興趣的能夠查看 AspectJExpressionPointcut#matches
既然是表達式,便比較關心Spring AOP都支持哪些表達式,從源碼中不難看出
public class AspectJExpressionPointcut extends AbstractExpressionPointcut implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware { private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>(); static { // execution() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION); // args() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS); // reference() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE); // this() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS); // target() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET); // within() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN); // @annotation() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION); // @within SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN); // @arges() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS); // @target() SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET); } }
具體的含義參考 Supported Pointcut Designators,用法參考 AOP Pointcut Examples,使用與、或、非運算參考 Combining Pointcut Expressions
看到composable應該就能猜到這是一個複合的Pointcut,能夠將多個Pointcut組合到一塊兒進行union、intersection運算
Advice
Advice的定義比較讓人摸不到頭腦,並無聲明任何方法
/** * Tag interface for Advice. Implementations can be any type * of advice, such as Interceptors. */ public interface Advice {}
可是上文已經講了,Spring AOP會根據不一樣的註解生成不一樣的Advice實現
註解 | Advice實現 |
---|---|
@Around | AspectJAroundAdvice |
@Before | AspectJMethodBeforeAdvice |
@After | AspectJAfterAdvice |
@AfterReturning | AspectJAfterReturningAdvice |
@AfterThrowing | AspectJAfterThrowingAdvice |
Spring AOP在生成以上任意一種Advice實現時,都會將
candidateAdviceMethod
以上各註解修飾的advice方法
expressionPointcut
上述生成的AspectJExpressionPointcut
aspectInstanceFactory
這是以後定位攔截的方法、生成代理類、執行代理邏輯必須的內容
除了AspectJMethodBeforeAdvice及AspectJAfterReturningAdvice以外,其餘三個Advice實現均實現了MethodInterceptor接口
public interface MethodInterceptor extends Interceptor { Object invoke(MethodInvocation invocation) throws Throwable; }
這即是攔截/代理的POINT,查看各Advice對invoke方法的實現邏輯
// AspectJAroundAdvice public Object invoke(MethodInvocation mi) throws Throwable { ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi; // 將目標方法封裝爲ProceedingJoinPoint ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi); JoinPointMatch jpm = getJoinPointMatch(pmi); // 將目標方法的ProceedingJoinPoint傳入advice方法進行調用 return invokeAdviceMethod(pjp, jpm, null, null); }
// AspectJAfterAdvice public Object invoke(MethodInvocation mi) throws Throwable { try { // 調用目標方法 return mi.proceed(); } finally { // 調用advice方法 invokeAdviceMethod(getJoinPointMatch(), null, null); } }
// AspectJAfterThrowingAdvice public Object invoke(MethodInvocation mi) throws Throwable { try { // 調用目標方法 return mi.proceed(); } catch (Throwable ex) { if (shouldInvokeOnThrowing(ex)) { // 調用advice方法 invokeAdviceMethod(getJoinPointMatch(), null, ex); } throw ex; } }
到這裏,彷佛對Spring AOP的流程已經有些眉目了,那AspectJMethodBeforeAdvice及AspectJAfterReturningAdvice是如何執行的?
其又分別對應另外兩個AdviceInterceptor(無辜臉~)
Advice實現 | 對應的MethodInterceptor實現 |
---|---|
AspectJMethodBeforeAdvice | MethodBeforeAdviceInterceptor |
AspectJAfterReturningAdvice | AfterReturningAdviceInterceptor |
再來看這兩種MethodInterceptor的invoke實現
// MethodBeforeAdviceInterceptor public Object invoke(MethodInvocation mi) throws Throwable { // 調用advice方法 this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // 調用目標方法 return mi.proceed(); }
// AfterReturningAdviceInterceptor public Object invoke(MethodInvocation mi) throws Throwable { // 調用目標方法 Object retVal = mi.proceed(); // 調用advice方法 this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis()); return retVal; }
因此,你有看懂@After和@AfterReturning的區別了麼?
Q: MethodBeforeAdviceInterceptor及AfterReturningAdviceInterceptor是在何時封裝的?
Advisor
沒有特別的邏輯在裏邊,封裝了上文提到的Pointcut實現及Advice實現
ReflectiveAspectJAdvisorFactory#getDeclareParentsAdvisor
主要用於對@DeclareParents修飾的屬性進行解析,用來支持Introduction
須要加強的接口爲@DeclareParents修飾的屬性類型,加強的接口實現爲@DeclareParents中的defaultImpl參數
AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply
AopUtils#findAdvisorsThatCanApply
經過上面的過程(每個節點都是存在緩存的,以上的邏輯只在首次觸發)已經拿到了容器中全部已註冊的Advisors,如今的流程還處在BeanPostProcessor#postProcessAfterInitialization中,只須要使用每個Advisor中Pointcut的ClassFilter及MethodMatcher去匹配當前的目標bean即可過濾出須要應用到當前Bean上的Advisor
這部分邏輯比較簡單,以文字描述其過程
AspectJAwareAdvisorAutoProxyCreator#extendAdvisors
AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary
若是當前的目標Bean存在匹配上的Advisors,則會在在Advisors列表中添加一個特殊的Advisor - ExposeInvocationInterceptor.ADVISOR
該Advisor的實現爲DefaultPointcutAdvisor,內部Pointcut爲TruePointcut(匹配任何類的任何方法),內部Advice爲ExposeInvocationInterceptor,而且Order是最高的
ExposeInvocationInterceptor用於暴露當前線程正在執行的MethodInvocation
AspectJAwareAdvisorAutoProxyCreator#sortAdvisors
Advice會繼承Aspect-Bean的Order(經過@Order註解,或實現Ordered或PriorityOrdered),對Advisors的排序則依賴以上(PriorityOrdered優先於Ordered及@Order)
因此,這裏有一個問題,若是在一個Aspect-Bean中定義多個Advices,則同一個Aspect-Bean中Advices的順序是沒有辦法定義/保障的(官方解釋 Advice Ordering)
AbstractAutoProxyCreator#createProxy
Spring AOP會經過proxyTargetClass參數的設置及實際Bean Class的狀況判斷使用Cglib仍是Jdk Proxy(這裏默認您對Cglib及Jdk Proxy的使用有必定的瞭解)
對Cglib來說,關鍵在於設置Enhancer的Callback,邏輯能夠參考CglibAopProxy#getCallbacks
對JDK Proxy來說,關鍵在於定義InvocationHandler,邏輯能夠參考JdkDynamicAopProxy#invoke
若是一個Bean中的方法匹配上多個Advice,那多個Advice的invoke方法是如何串行執行的?以JdkDynamicAopProxy中的邏輯爲例,有兩處代碼會比較有意思
// org.springframework.aop.framework.JdkDynamicAopProxy#invoke // 找到當前方法匹配到Advices List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // ... // 將以上Advices封裝爲ReflectiveMethodInvocation MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // 觸發整條鏈 retVal = invocation.proceed();
這裏相似於Servlet應用中的FilterChain,將advice chain一步步代入,以剝洋蔥的方式調用
上文提到的全部Advice實現均會實現MethodInterceptor,內部invoke方法的入參類型爲MethodInvocation,注意,ReflectiveMethodInvocation一樣實現了MethodInterceptor,簡單描述ReflectiveMethodInvocation#proceed的流程以下
關鍵在於各Advice的實現類中執行invoke方法時,調用的MethodInvocation.proceed既是ReflectiveMethodInvocation#proceed自己
若是存在順序的三個AroundAdvice、BeforeAdvice、AfterAdvice匹配同一個方法,整個調用過程將會是
Q: Spring AOP還提供了Listener支持(AdvisedSupportListener),如何使用?
@Aspect修飾的類必須同時註冊爲Bean
@Aspect @Component public class CatAspectJSupport { /* ... */}
攔截/切面/代理的優先級能夠經過@Order、Ordered、PriorityOrdered控制
@Aspect @Component @Order(2) public class CatAspectJSupport { /* ... */}
@Aspect @Component public class CatAspectJSupport implements Ordered /** PriorityOrdered **/ { @Override public int getOrder() { return 2; } }
Pointcut能夠經過@Pointcut註解聲明,也能夠直接經過@Around、@Before、@After、@AfterReturing、@AfterThrowing註解聲明
經過@Pointcut註解聲明能夠重複使用,經過Advice註解能夠將更多的參數引入Advice函數
@Pointcut("execution(* com.manerfan.springdemo.Cat.barkVoice(..))") public void barkVoiceMethod() {} @Around("barkVoiceMethod()") public Object statistics(ProceedingJoinPoint jp) { /* .. */} @Before("barkVoiceMethod())") public void beforeBark(JoinPoint jp) { /* .. */}
@Around("@annotation(statistics)") public Object statistics(ProceedingJoinPoint jp, Statistics statistics /* 直接將目標方法上的註解對象傳入 */) { /* .. */}
For matching method execution join points.
使用強大的表達式攔截方法
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
// 全部包中全部類的全部公共方法 execution(public * *(..)) // com.manerfan.service.AccountServiceImpl類中的全部方法 execution(* com.manerfan.service.AccountServiceImpl.*(..)) // com.manerfan.service包中(包含子包)全部類中返回Long類型的方法 execution(java.lang.Long com.manerfan.service..*.*(..)) // com.manerfan.service.AccountServiceImpl類中全部兩個參數且第二個參數是String類型的方法 execution(* com.manerfan.service.AccountServiceImpl.*(,java.lang.String))
注: 全類路徑須要是被代理的類路徑,非接口類路徑
Limits matching to join points within certain types (the execution of a method declared within a matching type when using Spring AOP).
能夠看作是execution的子集,用於匹配被攔截方法所在的類
// com.manerfan.service包下全部類中全部方法 within(com.manerfan.service.*) // com.manerfan.service包中(包含子包)全部類中全部方法 within(com.manerfan.service..*) // com.manerfan.service.AccountServiceImpl類中全部方法 within(com.manerfan.service.AccountServiceImpl)
注: 全類路徑須要是被代理的類路徑,非接口類路徑
Limits matching to join points (the execution of methods when using Spring AOP) where the bean reference (Spring AOP proxy) is an instance of the given type.
用於匹配生成的代理對象的類型
// 代理對象的類型爲com.manerfan.service.DogImpl this(com.manerfan.service.DogImpl) // 代理對象的類型爲com.manerfan.service.Animal接口的全部實現 this(com.manerfan.service.Animal)
注: 只能使用完整類路徑,不可以使用通配符,可使用實現類全路徑,也可使用接口全路徑
初此以外,還可使用參數匹配
// 匹配全部代理對象實現了Animal接口的類 @Before("this(animal)") public void animalBefore(JoinPoint jp, Animal animal /* 這裏代入的是生成的代理對象 */) { /* ... */ }
this
還有一個用途即是應用到Introduction上
// 爲com.manerfan.service包下的全部類生成代理對象的時候默認實現Poultry接口,實現類是DuckImpl @DeclareParents(value = "com.manerfan.service.*", defaultImpl = DuckImpl.class) public Poultry poultry; // 匹配全部代理對象實現了Poultry接口的類 @Before("this(poultry)") public void animalBefore(JoinPoint jp, Poultry poultry /* 這裏代入的是生成的代理對象 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the target object (application object being proxied) is an instance of the given type.
與this
相似,但不一樣的是,target
用於匹配目標Bean對象的類型
// 目標對象的類型爲com.manerfan.service.DogImpl target(com.manerfan.service.DogImpl) // 代理對象的類型爲com.manerfan.service.Animal接口的全部實現 target(com.manerfan.service.Animal)
注: 只能使用完整類路徑,不可以使用通配符,可使用實現類全路徑,也可使用接口全路徑
初此以外,還可使用參數匹配
// 匹配全部目標對象實現了Animal接口的類 @Before("target(animal)") public void animalBefore(JoinPoint jp, Animal animal /* 這裏代入的是目標Bean對象 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the arguments are instances of the given types.
用於匹配方法參數
// 匹配無參的函數 args() // 匹配只有一個參數,且類型爲String的函數 args(java.lang.String) // 匹配任意參數的函數 args(..) // 匹配任意參數且第一個參數爲String的函數 args(java.lang.String,..) // 匹配任意參數且最後一個參數爲String的函數 args(..,java.lang.String)
初此以外,還可使用參數匹配
// 匹配com.manerfan包下全部第一個參數是int類型的方法 @Before("within(com.manerfan..*) && args(index,...)") public void animalBefore(JoinPoint jp, int index /* 這裏代入目標方法的參數值 */) { /* ... */ }
Limits matching to join points (the execution of methods when using Spring AOP) where the class of the executing object has an annotation of the given type.
用於匹配目標類上的註解
// 匹配com.manerfan包下全部被Statistics註解修飾的類的全部方法 within(com.manerfan..*) && @target(com.manerfan.support.Statistics)
注: 只能使用註解的全路徑 ,不能使用通配符
初此以外,還可使用參數匹配
// 匹配com.manerfan包下全部被Statistics註解修飾的類的全部方法 @Before("within(com.manerfan..*) && @target(st)") public void animalBefore(JoinPoint jp, Statistics st /* 這裏代入目標類上的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上,不可應用於接口類,不可應用於方法
Limits matching to join points (the execution of methods when using Spring AOP) where the runtime type of the actual arguments passed have annotations of the given types.
用於匹配方法參數上的註解
// 匹配只有一個參數,且被Statistics註解修飾 @args(com.manerfan.support.Statistics) // 匹配任意參數且第一個參數被Statistics註解修飾 @args(com.manerfan.support.Statistics,..) // 匹配任意參數且最後一個參數被Statistics註解修飾 @args(..,com.manerfan.support.Statistics)
注: 只能使用註解的全路徑 ,不能使用通配符
初此以外,還可使用參數匹配
// 匹配com.manerfan包下全部類中全部第一個參數被Statistics註解修飾的方法 @Before("within(com.manerfan..*) && @args(st,..)") public void animalBefore(JoinPoint jp, Statistics st /* 這裏代入目標方法參數上的註解,能夠獲取註解中的值 */) { /* ... */ }
Limits matching to join points within types that have the given annotation (the execution of methods declared in types with the given annotation when using Spring AOP).
用於匹配被攔截方法所在的類上的註解
// 全部被Statistics註解修飾的類的方法 @within(com.manerfan.support.Statistics)
注: 只能使用註解的全路徑 ,不能使用通配符
初此以外,還可使用參數匹配
// 匹配com.manerfan包下 全部被Statistics註解修飾的類的方法 @Before("within(com.manerfan..*) && @within(st)") public void animalBefore(JoinPoint jp, Statistics st /* 這裏代入目標類的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上,不可應用於接口類,不可應用於方法
Limits matching to join points where the subject of the join point (the method being executed in Spring AOP) has the given annotation.
用於匹配被攔截方法上的註解
// 全部被Statistics註解修飾的方法 @annotation(com.manerfan.support.Statistics)
注: 只能使用註解的全路徑 ,不能使用通配符
初此以外,還可使用參數匹配
// 匹配com.manerfan包下 全部被Statistics註解修飾的方法 @Before("within(com.manerfan..*) && @annotation(st)") public void animalBefore(JoinPoint jp, Statistics st /* 這裏代入目標方法上的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上的方法上,不可應用於接口類方法,不可直接應用於目標類