2018-05-19

spring源碼分析(五)



目錄

五、源碼分析
--5.6、Spring AOP 設(shè)計原理及具體實踐
----5.6.1、SpringAOP 應(yīng)用示例
------AOP 的相關(guān)概念
------通知(Advice)類型
------使用 SpringAOP 兩種方式
--------表達式中使用”execution“
--------訪問當前的連接點
--------通知參數(shù)
----5.6.2、SpringAOP 設(shè)計原理及源碼分析



五、源碼分析

5.6、Spring AOP 設(shè)計原理及具體實踐
5.6.1、SpringAOP 應(yīng)用示例

AOP 是 OOP 的延續(xù),是 AspectOrientedProgramming 的縮寫,意思是面向切面編程??梢酝ㄟ^預(yù)編譯方式和運行期動態(tài)代 理實現(xiàn)在不修改源代碼的情況下給程序動態(tài)統(tǒng)一添加功能的一種技術(shù)。 AOP 設(shè)計模式孜孜不倦追求的是調(diào)用者和被調(diào)用者之 間的解耦,AOP 可以說也是這種目標的一種實現(xiàn)。

我們現(xiàn)在做的一些非業(yè)務(wù),如:日志、事務(wù)、安全等都會寫在業(yè)務(wù)代碼中(也即是說,這些非業(yè)務(wù)類橫切于業(yè)務(wù)類),但這些 代碼往往是重復(fù),復(fù)制——粘貼式的代碼會給程序的維護帶來不便,AOP 就實現(xiàn)了把這些業(yè)務(wù)需求與系統(tǒng)需求分開來做。這 種解決的方式也稱代理機制。

AOP 的相關(guān)概念

  • 切面(Aspect):官方的抽象定義為“一個關(guān)注點的模塊化,這個關(guān)注點可能會橫切多個對象”?!扒忻妗痹?ApplicationContext 中<aop:aspect>來配置。

  • 連接點(Joinpoint):程序執(zhí)行過程中的某一行為,例如,MemberService.get 的調(diào)用或者 MemberService.delete 拋出異常等行為。 ? 通知(Advice) :“切面”對于某個“連接點”所產(chǎn)生的動作。其中,一個“切面”可以包含多個“Advice”。

  • 切入點(Pointcut) :匹配連接點的斷言,在 AOP 中通知和一個切入點表達式關(guān)聯(lián)。切面中的所有通知所關(guān)注的連 接點,都由切入點表達式來決定。

  • 目標對象(TargetObject) :被一個或者多個切面所通知的對象。例如,AServcieImpl 和 BServiceImpl,當然在實 際運行時,SpringAOP 采用代理實現(xiàn),實際 AOP 操作的是 TargetObject 的代理對象。

  • AOP 代理(AOPProxy):在 SpringAOP 中有兩種代理方式, JDK 動態(tài)代理和 CGLIB 代理。默認情況下, TargetObject 實現(xiàn)了接口時,則采用 JDK 動態(tài)代理,例如,AServiceImpl;反之,采用 CGLIB 代理,例如,BServiceImpl。強制 使用 CGLIB 代理需要將 <aop:config>的 proxy-target-class 屬性設(shè)為 true。

通知(Advice)類型:

  • 前置通知(Beforeadvice):在某連接點(JoinPoint)之前執(zhí)行的通知,但這個通知不能阻止連接點前的執(zhí)行。 ApplicationContext 中在<aop:aspect>里面使用<aop:before>元素進行聲明。例如,TestAspect 中的 doBefore 方法。

  • 后置通知(Afteradvice):當某連接點退出的時候執(zhí)行的通知(不論是正常返回還是異常退出)。ApplicationContext 中在<aop:aspect>里面使用<aop:after>元素進行聲明。例如,ServiceAspect 中的 returnAfter 方法,所以 Teser 中調(diào)用 UserService.delete 拋出異常時,returnAfter 方法仍然執(zhí)行。

  • 返回后通知(Afterreturnadvice):在某連接點正常完成后執(zhí)行的通知,不包括拋出異常的情況。ApplicationContext 中在<aop:aspect>里面使用<after-returning>元素進行聲明。

  • 環(huán)繞通知(Aroundadvice):包圍一個連接點的通知,類似 Web 中 Servlet 規(guī)范中的 Filter 的 doFilter 方法。可以在 方法的調(diào)用前后完成自定義的行為,也可以選擇不執(zhí)行。ApplicationContext 中在<aop:aspect>里面使用<aop:around> 元素進行聲明。例如,ServiceAspect 中的 around 方法。

  • 拋出異常后通知(Afterthrowingadvice):在方法拋出異常退出時執(zhí)行的通知。ApplicationContext 中在<aop:aspect> 里面使用<aop:after-throwing>元素進行聲明。例如,ServiceAspect 中的 returnThrow 方法。

注:可以將多個通知應(yīng)用到一個目標對象上,即可以將多個切面織入到同一目標對象。

使用 SpringAOP 可以基于兩種方式:

  • 一種是比較方便和強大的注解方式
  • 另一種則是中規(guī)中矩的 xml 配置方式。

注解,使用注解配置 SpringAOP 總體分為兩步:

第一步是在 xml 文件中聲明激活自動掃描組件功能,同時激活自動代 理功能(來測試 AOP 的注解功能):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-2.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    
<context:component-scan base-package="com.gupaoedu"/>
<context:annotation-config />
    
</beans>

第二步是為 Aspect 切面類添加注解:

//聲明這是一個組件
@Component
//聲明這是一個切面Bean
@Aspect
public class AnnotaionAspect {
    private final static Logger log = Logger.getLogger(AnnotaionAspect.class);
    //配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
    @Pointcut("execution(* com.gupaoedu.aop.service..*(..))")
    public void aspect(){ }
    /*
    * 配置前置通知,使用在方法aspect()上注冊的切入點
    * 同時接受JoinPoint切入點對象,可以沒有該參數(shù)
    */
    @Before("aspect()")
    public void before(JoinPoint joinPoint){
        log.info("before " + joinPoint);
    }
    //配置后置通知,使用在方法aspect()上注冊的切入點
    @After("aspect()")
    public void after(JoinPoint joinPoint){
        log.info("after " + joinPoint);
    }
    //配置環(huán)繞通知,使用在方法aspect()上注冊的切入點
    @Around("aspect()")
    public void around(JoinPoint joinPoint){
        long start = System.currentTimeMillis();
        try {
            ((ProceedingJoinPoint) joinPoint).proceed();
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
        } catch (Throwable e) {
            long end = System.currentTimeMillis();
            log.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
        }
    }
    //配置后置返回通知,使用在方法aspect()上注冊的切入點
    @AfterReturning("aspect()")
    public void afterReturn(JoinPoint joinPoint){
        log.info("afterReturn " + joinPoint);
    }
    //配置拋出異常后通知,使用在方法aspect()上注冊的切入點
    @AfterThrowing(pointcut="aspect()", throwing="ex")
    public void afterThrow(JoinPoint joinPoint, Exception ex){
        log.info("afterThrow " + joinPoint + "\t" + ex.getMessage());
    }
}

測試代碼

@ContextConfiguration(locations = {"classpath*:application-context.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
public class AnnotationTester {
    @Autowired MemberService annotationService;
    @Autowired ApplicationContext app;
    @Test
    // @Ignore
    public void test(){
        System.out.println("=====這是一條華麗的分割線======");
        AnnotaionAspect aspect = app.getBean(AnnotaionAspect.class);
        System.out.println(aspect);
        annotationService.save(new Member());
        System.out.println("=====這是一條華麗的分割線======");
        try {
            annotationService.delete(1L);
        } catch (Exception e) {
            //e.printStackTrace();
        }
    }
}

控制臺輸出如下:

=====這是一條華麗的分割線======
com.gupaoedu.aop.aspect.AnnotaionAspect@6ef714a0
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - before execution(void
com.gupaoedu.aop.service.MemberService.save(Member))
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.ArgsAspect - beforeArgUser execution(void
com.gupaoedu.aop.service.MemberService.save(Member))
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - save member method . . . [INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - around execution(void
com.gupaoedu.aop.service.MemberService.save(Member)) Use time : 38 ms!
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - after execution(void
com.gupaoedu.aop.service.MemberService.save(Member))
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - afterReturn execution(void
com.gupaoedu.aop.service.MemberService.save(Member)) =====這是一條華麗的分割線======
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - before execution(boolean
com.gupaoedu.aop.service.MemberService.delete(long))
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.ArgsAspect - beforeArgId execution(boolean
com.gupaoedu.aop.service.MemberService.delete(long)) ID:1
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - delete method . . . [INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - around execution(boolean
com.gupaoedu.aop.service.MemberService.delete(long)) Use time : 3 ms with exception : spring aop
ThrowAdvice演示
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - after execution(boolean
com.gupaoedu.aop.service.MemberService.delete(long))
[INFO ] [13:04:46] com.gupaoedu.aop.aspect.AnnotaionAspect - afterReturn execution(boolean
com.gupaoedu.aop.service.MemberService.delete(long))

可以看到,正如我們預(yù)期的那樣,雖然我們并沒有對 MemberService 類包括其調(diào)用方式做任何改變,但是 Spring 仍然攔截到了其中方法的調(diào)用,或許這正是 AOP 的魔力所在。

再簡單說一下 xml 配置方式,其實也一樣簡單:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
    
    <aop:aspectj-autoproxy proxy-target-class="true"/>

    <bean id="xmlAspect" class="com.gupaoedu.aop.aspect.XmlAspect"></bean>

    <!-- AOP配置 -->
    <aop:config>
        <!-- 聲明一個切面,并注入切面Bean,相當于@Aspect -->
        <aop:aspect ref="xmlAspect">
            <!-- 配置一個切入點,相當于@Pointcut -->
            <aop:pointcut expression="execution(* com.gupaoedu.aop.service..*(..))" id="simplePointcut"/>
            <!-- 配置通知,相當于@Before、@After、@AfterReturn、@Around、@AfterThrowing -->
            <aop:before pointcut-ref="simplePointcut" method="before"/>
            <aop:after pointcut-ref="simplePointcut" method="after"/>
            <aop:after-returning pointcut-ref="simplePointcut" method="afterReturn"/>
            <aop:after-throwing pointcut-ref="simplePointcut" method="afterThrow" throwing="ex"/>
        </aop:aspect>
    </aop:config>
</beans>

個人覺得不如注解靈活和強大,你可以不同意這個觀點,但是不知道如下的代碼會不會讓你的想法有所改善:

//配置切入點,該方法無方法體,主要為方便同類中其他方法使用此處配置的切入點
@Pointcut("execution(* com.gupaoedu.aop.service..*(..))")
public void aspect(){ }

//配置前置通知,攔截返回值為cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(com.gupaoedu.model.Member com.gupaoedu.aop.service..*(..))")
public void beforeReturnUser(JoinPoint joinPoint){
    log.info("beforeReturnUser " + joinPoint);
}

//配置前置通知,攔截參數(shù)為cn.ysh.studio.spring.mvc.bean.User的方法
@Before("execution(* com.gupaoedu.aop.service..*(com.gupaoedu.model.Member))")
public void beforeArgUser(JoinPoint joinPoint){
    log.info("beforeArgUser " + joinPoint);
}

//配置前置通知,攔截含有l(wèi)ong類型參數(shù)的方法,并將參數(shù)值注入到當前方法的形參id中
@Before("aspect()&&args(id)")
public void beforeArgId(JoinPoint joinPoint, long id){
    log.info("beforeArgId " + joinPoint + "\tID:" + id);
}

以下是 MemberService 的代碼:

@Service
public class MemberService {
    private final static Logger log = Logger.getLogger(AnnotaionAspect.class);
    public Member get(long id){
        log.info("getMemberById method . . .");
        return new Member();
    }
    public Member get(){
        log.info("getMember method . . .");
        return new Member();
    }
    public void save(Member member){
        log.info("save member method . . .");
    }
    public boolean delete(long id) throws Exception{
        log.info("delete method . . .");
        throw new Exception("spring aop ThrowAdvice演示");
    }
}

應(yīng)該說學習 Spring AOP 有兩個難點,第一點在于理解 AOP 的理念和相關(guān)概念,第二點在于靈活掌握和使用切入點表達式。

概念的理解通常不在一朝一夕,慢慢浸泡的時間長了,自然就明白了,下面我們簡單地介紹一下切入點表達式的配置規(guī)則吧。

通常情況下,表達式中使用”execution“就可以滿足大部分的要求。表達式格式如下:

execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?
  • modifiers-pattern:方法的操作權(quán)限
  • ret-type-pattern:返回值
  • declaring-type-pattern:方法所在的包
  • name-pattern:方法名
  • parm-pattern:參數(shù)名
  • throws-pattern:異常

其中,除 ret-type-pattern 和 name-pattern 之外,其他都是可選的。上例中,execution(com.spring.service..*(..))表示 com.spring.service 包下,返回值為任意類型;方法名任意;參數(shù)不作限制的所有方法。

通知參數(shù)

可以通過 args 來綁定參數(shù),這樣就可以在通知(Advice)中訪問具體參數(shù)了。例如,<aop:aspect>配置如下:

<aop:config>
    <aop:aspect ref="xmlAspect">
        <aop:pointcut id="simplePointcut" expression="execution(* com.gupaoedu.aop.service..*(..)) and args(msg,..)" />
        <aop:after pointcut-ref="simplePointcut" method="after"/>
    </aop:aspect>
</aop:config>

上面的代碼 args(msg,..)是指將切入點方法上的第一個 String 類型參數(shù)添加到參數(shù)名為 msg 的通知的入?yún)⑸?,這樣就可以直接 使用該參數(shù)啦。

訪問當前的連接點

在上面的 Aspect 切面 Bean 中已經(jīng)看到了,每個通知方法第一個參數(shù)都是 JoinPoint。其實,在 Spring 中,任何通知(Advice) 方法都可以將第一個參數(shù)定義為 org.aspectj.lang.JoinPoint 類型用以接受當前連接點對象。JoinPoint 接口提供了一系列有用 的方法, 比如 getArgs() (返回方法參數(shù))、getThis() (返回代理對象)、getTarget() (返回目標)、getSignature() (返回 正在被通知的方法相關(guān)信息)和 toString() (打印出正在被通知的方法的有用信息)。

5.6.2、SpringAOP 設(shè)計原理及源碼分析

開始之前先上圖,看看 Spring 中主要的 AOP 組件


AOP組件.PNG

Spring 提供了兩種方式來生成代理對象: JDKProxy 和 Cglib,具體使用哪種方式生成由 AopProxyFactory 根據(jù) AdvisedSupport 對象的配置來決定。默認的策略是如果目標類是接口,則使用 JDK 動態(tài)代理技術(shù),否則使用 Cglib 來生成代 理。下面我們來研究一下 Spring 如何使用 JDK 來生成代理對象,具體的生成代碼放在 JdkDynamicAopProxy 這個類中,直接 上相關(guān)代碼:

/**
* <ol>
* <li>獲取代理類要實現(xiàn)的接口,除了 Advised 對象中配置的,還會加上 SpringProxy, Advised(opaque=false)
* <li>檢查上面得到的接口中有沒有定義 equals 或者 hashcode 的接口
* <li>調(diào)用 Proxy.newProxyInstance 創(chuàng)建代理對象
* </ol>
*/
public Object getProxy(ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " +
        this.advised.getTargetSource());
    }
    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

那這個其實很明了,注釋上我也已經(jīng)寫清楚了,不再贅述。

下面的問題是,代理對象生成了,那切面是如何織入的?

我們知道 InvocationHandler 是 JDK 動態(tài)代理的核心,生成的代理對象的方法調(diào)用都會委托到 InvocationHandler.invoke() 方法。而通過 JdkDynamicAopProxy 的簽名我們可以看到這個類其實也實現(xiàn)了 InvocationHandler,下面我們就通過分析這個 類中實現(xiàn)的 invoke()方法來具體看下 Spring AOP 是如何織入切面的。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;
    
    TargetSource targetSource = this.advised.targetSource;
    Class targetClass = null;
    Object target = null;
    
    try {
        //eqauls()方法,具目標對象未實現(xiàn)此方法
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
        }
        //hashCode()方法,具目標對象未實現(xiàn)此方法
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
        }
        //Advised 接口或者其父接口中定義的方法,直接反射調(diào)用,不應(yīng)用通知
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
            method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // Service invocations on ProxyConfig with the proxy config...
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
        Object retVal;
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // May be null. Get as late as possible to minimize the time we "own" the target,
        // in case it comes from a pool.
        //獲得目標對象的類
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        // Get the interception chain for this method.
        //獲取可以應(yīng)用到此方法上的 Interceptor 列表
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,
        targetClass);
        
        // Check whether we have any advice. If we don t, we can fallback on direct
        // reflective invocation of the target, and avoid creating a MethodInvocation.
        //如果沒有可以應(yīng)用到此方法的通知(Interceptor),此直接反射調(diào)用 method.invoke(target, args)
        
        if (chain.isEmpty()) {
            // We can skip creating a MethodInvocation: just invoke the target directly
            // Note that the final invoker must be an InvokerInterceptor so we know it does
            // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }else {
            // We need to create a method invocation...
            //創(chuàng)建 MethodInvocation
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass,
            chain);
            // Proceed to the joinpoint through the interceptor chain.
            retVal = invocation.proceed();
        }
        // Massage return value if necessary.
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
        !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can t help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
        } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException("Null return value from advice does not match
            primitive return type for: " + method);
        }
        return retVal;
    }finally {
        if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

主流程可以簡述為:獲取可以應(yīng)用到此方法上的通知鏈(Interceptor Chain),如果有,則應(yīng)用通知,并執(zhí)行 joinpoint; 如 果沒有,則直接反射執(zhí)行 joinpoint。而這里的關(guān)鍵是通知鏈是如何獲取的以及它又是如何執(zhí)行的,下面逐一分析下。

首先,從上面的代碼可以看到,通知鏈是通過Advised.getInterceptorsAndDynamicInterceptionAdvice()這個方法來獲取 的,我們來看下這個方法的實現(xiàn):

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass){
    MethodCacheKey cacheKey = new MethodCacheKey(method);
    List<Object> cached = this.methodCache.get(cacheKey);
    if (cached == null) {
        cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
        this, method, targetClass);
        this.methodCache.put(cacheKey, cached);
    }
    return cached;
}

可以看到實際的獲取工作其實是由 AdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice()這個方法來 完成的,獲取到的結(jié)果會被緩存。

下面來分析下這個方法的實現(xiàn):

/**
* 從提供的配置實例 config 中獲取 advisor 列表,遍歷處理這些 advisor.如果是 IntroductionAdvisor,
* 則判斷此 Advisor 能否應(yīng)用到目標類 targetClass 上.如果是 PointcutAdvisor,則判斷
* 此 Advisor 能否應(yīng)用到目標方法 method 上.將滿足條件的 Advisor 通過 AdvisorAdaptor 轉(zhuǎn)化成 Interceptor
列表返回.
*/
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, Class targetClass) {
    // This is somewhat tricky... we have to process introductions first,
    // but we need to preserve order in the ultimate list.
    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    //查看是否包含 IntroductionAdvisor
    boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
    //這里實際上注冊一系列 AdvisorAdapter,用于將 Advisor 轉(zhuǎn)化成 MethodInterceptor
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    for (Advisor advisor : config.getAdvisors()) {
        if (advisor instanceof PointcutAdvisor) {
            // Add it conditionally.
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            if (config.isPreFiltered() ||
            pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                //這個地方這兩個方法的位置可以互換下
                //將 Advisor 轉(zhuǎn)化成 Interceptor
                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                //檢查當前 advisor 的 pointcut 是否可以匹配當前方法
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                if (MethodMatchers.matches(mm, method, targetClass, hasIntroductions)) {
                    if (mm.isRuntime()) {
                        // Creating a new object instance in the getInterceptors() method
                        // isn't a problem as we normally cache created chains.
                        for (MethodInterceptor interceptor : interceptors) {
                            interceptorList.add(new
                            InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }
            }
        }else if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            if (config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }else {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }
    return interceptorList;
}

這個方法執(zhí)行完成后,Advised 中配置能夠應(yīng)用到連接點或者目標類的 Advisor 全部被轉(zhuǎn)化成了 MethodInterceptor. 接下來我們再看下得到的攔截器鏈是怎么起作用的。

......
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
//如果沒有可以應(yīng)用到此方法的通知(Interceptor),此直接反射調(diào)用 method.invoke(target, args)
if (chain.isEmpty()) {
    // We can skip creating a MethodInvocation: just invoke the target directly
    // Note that the final invoker must be an InvokerInterceptor so we know it does
    // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
    // We need to create a method invocation...
    //創(chuàng)建 MethodInvocation
    invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
    // Proceed to the joinpoint through the interceptor chain.
    retVal = invocation.proceed();
}
......

從這段代碼可以看出,如果得到的攔截器鏈為空,則直接反射調(diào)用目標方法,否則創(chuàng)建 MethodInvocation,調(diào)用其 proceed方法,觸發(fā)攔截器鏈的執(zhí)行,來看下具體代碼

public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    //如果 Interceptor 執(zhí)行完了,則執(zhí)行 joinPoint
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }
    Object interceptorOrInterceptionAdvice =
    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    //如果要動態(tài)匹配 joinPoint
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
        (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        //動態(tài)匹配:運行時參數(shù)是否滿足匹配條件
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            //執(zhí)行當前 Intercetpor
            return dm.interceptor.invoke(this);
        }else {
            // Dynamic matching failed.
            // Skip this interceptor and invoke the next in the chain.
            //動態(tài)匹配失敗時,略過當前 Intercetpor,調(diào)用下一個 Interceptor
            return proceed();
        }
    }else {
        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        //執(zhí)行當前 Intercetpor
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容