【修煉內功】[spring-framework] [5] Spring AOP 是如何代理的

本文已收錄 【修煉內功】躍遷之路

spring-framework.jpg

林中小舍.png

在以前的文章中介紹了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

  • 可直接編碼增長一個代理層實現目標代碼的代理調用,此過程發生在編譯階段,稱之爲靜態代理(可參見java-desion-patterns-proxy
  • 可藉助jdk-proxy、cglib等技術生成代理類,並經過反射機制對目標代碼進行代理調用,此過程發生在運行階段,稱之爲動態代理(可參見commons-proxy中的各工具)
  • 也可藉助javassist、asm等字節碼加強技術直接修改(加強)目標字節碼,這種方式相似於編程語言中的內聯(inline),嚴格來說並不屬於編程模式的範疇

AOP

維基百科-面向切面的程序設計

Aspect-Oriented Programming,面向切面的程序設計,是計算機科學中的一種程序設計思想,旨在將橫切關注點與業務主體進行進一步分離,以提升程序代碼的模塊化程度。經過在現有代碼基礎上增長額外的通知(Advice)機制,可以對被聲明爲「切點(Pointcut)」的代碼塊進行統一管理與裝飾。git

從AOP的定義上來看,其與代理模式十分類似,那能夠說AOP就是代理模式的一種實現麼?並不是如此,AOP相比於代理模式表現的更爲靈活也更細膩,典型的框架如 AspectJgithub

Spring AOP

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的實現邏輯緩存

  1. Spring AOP是在哪一個階段生成代理類的?
  2. Spring AOP是如何找到須要代理的方法的?
  3. Spring AOP是如何(在代理類中)封裝代理方法的?
  4. 若是一個被代理方法匹配多個代理規則,不一樣的代理規則是如何依次執行的(invoke chain)?

Spring AOP 基礎概念

在深刻Spring AOP的實現原理以前,先熟悉幾個AOP的基本概念 (AOP Concepts)

  • Pointcut

    切入點,在哪些類的哪些方法上進行切入(攔截),其一般表現爲一個表達式(AspectJExpressionPointcut),經過該表達式匹配哪些類(ClassFilter)及哪些方法(MethodMatcher)須要被攔截代理

  • JoinPoint

    鏈接點,因爲Spring AOP僅支持方法,在Spring AOP中能夠理解爲,經過Pointcut匹配到的須要被攔截的方法

  • Advice

    通知,攔截到鏈接點後,須要代理執行的代碼邏輯,其一共分爲 AroundBeforeAfterAfterReturningAfterThrowing 五類

  • Aspect

    切面,即 Pointcut + Advice,在哪些類的哪些方法上,代理執行哪些邏輯

  • Weaving

    織入,將切面應用到目標對象,生成代理對象的過程

  • Target Object

    目標(被代理)類對象,鏈接點所在的類對象

  • Proxy Object

    生成的代理對象

  • Introduction

    引入,在不修改目標類代碼的前提下,可在運行期動態的增長一些方法(或屬性)

Spring AOP 註冊

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;
}

proxyTargetClassexposeProxy參數的做用參見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實例化且初始化完成以後

AOPCreate

AnnotationAwareAspectJAutoProxyCreator實際直接實現了 InstantiationAwareBeanPostProcessor接口,在目標對象建立以前便會有一個短路的操做,可能直接在該步驟生成代理對象而不須要目標對象的實例化,具體邏輯能夠參考 AbstractAutoProxyCreator#postProcessBeforeInstantiation

Spring AOP 切面的匹配

查看wrapIfNecessary的邏輯,總體上並不複雜,找到該bean全部匹配的切面Advisors,並根據所匹配的切面建立代理bean

wrapIfNecessary

首選關心的是,如何找到全部匹配的Advisors

AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean

AbstractAdvisorAutoProxyCreator#findEligibleAdvisors

findEligibleAdvisors

獲取全部已註冊的Advisors

AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors

Advisor的註冊有兩大途徑,直接註冊Advisor類型的Bean 或者 註冊被@Aspect修飾的Bean

findCandidateAdvisors

找到全部Advisor類型的Beans

AbstractAdvisorAutoProxyCreator#findCandidateAdvisors

BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

這一步驟其實並不複雜,在BeanFactory中找到全部註冊爲Advisor類型的Bean並提早初始化

findAdvisorBeans

查看Advisor的源碼十分簡單,只有一個方法會比較感興趣

public interface Advisor {
    Advice getAdvice();
}

而Advice則顯得更加神祕,沒有任何方法接口定義

public interface Advice {}

留個懸念,客官且看

Q: Advisor是什麼?Advice的實現類都有哪些?如何直接註冊Advisor的Bean?經過註冊Advisor Bean來實現AOP的場景有哪些?

找到全部@Aspect修飾的Beans並解析

BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors

buildAspectJAdvisors

Spring將全部註冊的bean-names拿出遍歷,過濾出被@Aspect註解修飾的Bean,經過@Apsect註解及Aspect-Bean信息生成BeanFactoryAspectInstanceFactory,用於後續Advisor的解析(ReflectiveAspectJAdvisorFactory#getAdvisors

getAdvisors

從流程圖能夠看出,Advisor的解析主要來自方法及屬性兩部分,方法主要用來解析@Around@Before@After@AfterReturing@AfterThrowing,屬性主要用來解析@DeclareParents

方法解析

ReflectiveAspectJAdvisorFactory#getAdvisor

getAdvisor

Spring會查找被@Around@Before@After@AfterReturing@AfterThrowing(若是一個方法同時被多個註解修飾,按順序只會匹配第一個被找到的註解)修飾的方法,將註解的信息封裝爲AspectJExpressionPointcut,將方法的內部邏輯封裝爲不一樣類型的xxxAdvice,並最終將上述的PointcutAdvice組合封裝爲InstantiationModelAwarePointcutAdvisorImpl提供出去

這裏出現了開篇提到的幾個概念 PointcutAdvice

Pointcut

Pointcut

Pointcut的實現很是多,深刻以前先瀏覽下Pointcut的接口定義

public interface Pointcut {
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
}

不難猜測,Spring AOP就是經過這兩個方法來判斷哪些類的哪些方法須要被代理,反過來說經過這兩個方法判斷當前Bean的各個方法有沒有以及匹配上了哪些Advice

衆多的Pointcut實現中,重點介紹其中的幾個

  • TruePointcut

    該Pointcut的實現很是簡單,不管入參是什麼,ClassFilter及MethodMatcher的matches方法總會返回true

  • AnnotationMatchingPointcut

    該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

  • ComposablePointcut

    看到composable應該就能猜到這是一個複合的Pointcut,能夠將多個Pointcut組合到一塊兒進行unionintersection運算

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
    上文講的經過@Apsect註解及Aspect-Bean信息生成BeanFactoryAspectInstanceFactory做爲構造參數傳入

這是以後定位攔截的方法、生成代理類、執行代理邏輯必須的內容

除了AspectJMethodBeforeAdviceAspectJAfterReturningAdvice以外,其餘三個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的流程已經有些眉目了,那AspectJMethodBeforeAdviceAspectJAfterReturningAdvice是如何執行的?

其又分別對應另外兩個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的區別了麼?

Advice

Q: MethodBeforeAdviceInterceptor及AfterReturningAdviceInterceptor是在何時封裝的?
Advisor

沒有特別的邏輯在裏邊,封裝了上文提到的Pointcut實現及Advice實現

屬性解析

ReflectiveAspectJAdvisorFactory#getDeclareParentsAdvisor

getDeclareParentsAdvisor

主要用於對@DeclareParents修飾的屬性進行解析,用來支持Introduction

須要加強的接口爲@DeclareParents修飾的屬性類型,加強的接口實現爲@DeclareParents中的defaultImpl參數

匹配符合條件的Advisors

AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply

AopUtils#findAdvisorsThatCanApply

經過上面的過程(每個節點都是存在緩存的,以上的邏輯只在首次觸發)已經拿到了容器中全部已註冊的Advisors,如今的流程還處在BeanPostProcessor#postProcessAfterInitialization中,只須要使用每個Advisor中Pointcut的ClassFilter及MethodMatcher去匹配當前的目標bean即可過濾出須要應用到當前Bean上的Advisor

這部分邏輯比較簡單,以文字描述其過程

  1. 若是是IntroductionAdvisor,則取出ClassFilter對當前Bean進行match
  2. 若是是PointcutAdvisor,則取出ClassFilter對當前Bean進行match,經過後依次遍歷當前Bean-Class中的每個方法,使用MethodMatcher進行match,只要有一個方法匹配上則經過

添加額外的Advisor

AspectJAwareAdvisorAutoProxyCreator#extendAdvisors

AspectJProxyUtils#makeAdvisorChainAspectJCapableIfNecessary

若是當前的目標Bean存在匹配上的Advisors,則會在在Advisors列表中添加一個特殊的Advisor - ExposeInvocationInterceptor.ADVISOR

該Advisor的實現爲DefaultPointcutAdvisor,內部Pointcut爲TruePointcut(匹配任何類的任何方法),內部Advice爲ExposeInvocationInterceptor,而且Order是最高的

ExposeInvocationInterceptor用於暴露當前線程正在執行的MethodInvocation

對Advisors進行排序

AspectJAwareAdvisorAutoProxyCreator#sortAdvisors

Advice會繼承Aspect-Bean的Order(經過@Order註解,或實現Ordered或PriorityOrdered),對Advisors的排序則依賴以上(PriorityOrdered優先於Ordered及@Order)

因此,這裏有一個問題,若是在一個Aspect-Bean中定義多個Advices,則同一個Aspect-Bean中Advices的順序是沒有辦法定義/保障的(官方解釋 Advice Ordering

Spring AOP 織入

AbstractAutoProxyCreator#createProxy

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的流程以下

ReflectiveMethodInvocation.proceed

關鍵在於各Advice的實現類中執行invoke方法時,調用的MethodInvocation.proceed既是ReflectiveMethodInvocation#proceed自己

若是存在順序的三個AroundAdvice、BeforeAdvice、AfterAdvice匹配同一個方法,整個調用過程將會是

ReflectiveMethodInvocation.demo

Q: Spring AOP還提供了Listener支持(AdvisedSupportListener),如何使用?

小結

  1. 註冊切面能夠經過@Aspcet註解、xml配置或者Advisor類型的Bean
  2. Spring AOP會尋找@Aspect修飾的Bean,並在其中尋找@Around@Before@After@AfterReturing@AfterThrowing修飾的方法及@DeclareParents修飾的屬性
  3. Aspect Bean中符合條件的方法/屬性會被封裝爲Advisor(Pointcut + Advice)
  4. Spring AOP經過BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)實現目標Bean的代理建立
  5. Spring AOP經過Pointcut中的ClassFilterMethodMatcher匹配目標Bean須要應用的Advisor
  6. Spring AOP經過proxyTargetClass參數的設置及實際Bean Class的狀況判斷使用Cglib仍是Jdk Proxy
  7. 若是一個Bean中的方法匹配上多個Advice,那多個Advice的invoke方法經過ReflectiveMethodInvocation進行鏈式調用

Spring AOP 註解使用概括

@Aspect

@Aspect修飾的類必須同時註冊爲Bean

@Aspect
@Component
public class CatAspectJSupport { /* ... */}

@Order

攔截/切面/代理的優先級能夠經過@OrderOrderedPriorityOrdered控制

@Aspect
@Component
@Order(2)
public class CatAspectJSupport { /* ... */}
@Aspect
@Component
public class CatAspectJSupport implements Ordered /** PriorityOrdered **/ {
    @Override
    public int getOrder() { return 2; }
}

@Point

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 /* 直接將目標方法上的註解對象傳入 */)  { /* .. */}

execution

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))
注: 全類路徑須要是被代理的類路徑,非接口類路徑

within

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)
注: 全類路徑須要是被代理的類路徑,非接口類路徑

this

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 /* 這裏代入的是生成的代理對象 */) { /* ... */ }

target

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對象 */) { /* ... */ }

args

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 /* 這裏代入目標方法的參數值 */) { /* ... */ }

@target

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 /* 這裏代入目標類上的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上,不可應用於接口類,不可應用於方法

@args

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 /* 這裏代入目標方法參數上的註解,能夠獲取註解中的值 */) { /* ... */ }

@within

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 /* 這裏代入目標類的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上,不可應用於接口類,不可應用於方法

@annotation

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 /* 這裏代入目標方法上的註解,能夠獲取註解中的值 */) { /* ... */ }
注: 註解只能應用於目標類上的方法上,不可應用於接口類方法,不可直接應用於目標類

訂閱號

相關文章
相關標籤/搜索