Spring Aop之Jdk代理實(shí)現(xiàn)原理詳解

Jdk代理,也稱為動(dòng)態(tài)代理,其代理目標(biāo)對(duì)象的方式是生成一個(gè)與目標(biāo)對(duì)象實(shí)現(xiàn)同一個(gè)接口的類,該類的構(gòu)造函數(shù)中會(huì)傳入一個(gè) InvocationHandler 類型的對(duì)象。因?yàn)?InvocationHandler 對(duì)象是用戶自定義的織入了切面邏輯的類,因而在需要使用目標(biāo)對(duì)象的地方,只需要將生成的代理類的對(duì)象傳入即可。

又因?yàn)樯傻拇眍惻c目標(biāo)類都實(shí)現(xiàn)了同一接口,因而從語(yǔ)法上其是沒有任何問題的。另一方面,在傳入代理類對(duì)象之后,代理類通過調(diào)用構(gòu)造函數(shù)傳入的InvocationHandler.invoke() 方法,從而動(dòng)態(tài)的調(diào)用目標(biāo)類的方法,最終利用這種方式織入了代理邏輯。

1. 代理對(duì)象的構(gòu)造

前面我們講到,Spring Aop使用 AopProxy.getProxy() 方法獲取代理對(duì)象的,而 JdkDynamicAopProxy 則已經(jīng)實(shí)現(xiàn)了該接口,因而我們可以直接閱讀其 getProxy() 方法的源碼:

public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
        logger.debug("Creating JDK dynamic proxy: target source is " 
            + this.advised.getTargetSource());
    }
    // 完善代理對(duì)象需要實(shí)現(xiàn)的接口,主要是會(huì)默認(rèn)增加三個(gè)需要實(shí)現(xiàn)的接口:SpringProxy,
    // Advised和DecoratingProxy。這三個(gè)接口的作用主要如下:
    // SpringProxy:該接口沒有任何方法,主要用于標(biāo)識(shí)當(dāng)前對(duì)象是Spring生成的代理對(duì)象;
    // Advised:用于封裝生成代理對(duì)象所需要的所有信息;
    // DecoratingProxy:其有一個(gè)getDecoratedClass()方法,用于返回當(dāng)前代理對(duì)象的目標(biāo)對(duì)象的Class類型
    Class<?>[] proxiedInterfaces = AopProxyUtils
        .completeProxiedInterfaces(this.advised, true);
    // 找到接口中是否包含有equals()和hashCode()方法,并進(jìn)行標(biāo)識(shí)
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    // 使用動(dòng)態(tài)代理生成代理對(duì)象
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

上述方法邏輯其實(shí)比較簡(jiǎn)單,主要就是首先獲取生成代理對(duì)象所需要實(shí)現(xiàn)的所有接口;然后判斷接口中是否包含有equals()和hashCode()方法,因?yàn)檫@將用于判斷生成的代理類是使用用于定義的equals()和hashCode()方法還是使用自動(dòng)生成的方法;最后就是通過動(dòng)態(tài)代理生成代理對(duì)象,這里需要注意的是 JdkDynamicAopProxy 不僅實(shí)現(xiàn)了AopProxy 接口還實(shí)現(xiàn)了 InvocationHandler 接口,因而這里生成代理對(duì)象的時(shí)候傳入的 InvocationHandler 對(duì)象是this。

2. 切面邏輯的織入

由于 JdkDynamicAopProxy 已經(jīng)實(shí)現(xiàn)了 InvocationHandler 接口,因而代理邏輯的織入就是在JdkDynamicAopProxy.invoke() 方法中,這里我們直接閱讀器源碼:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    MethodInvocation invocation;
    Object oldProxy = null;
    boolean setProxyContext = false;

    TargetSource targetSource = this.advised.targetSource;
    Object target = null;

    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 如果當(dāng)前方法是equals()方法,并且接口中并未定義該方法,就使用自動(dòng)生成的equals()方法
            return equals(args[0]);
        } else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 如果當(dāng)前方法是hashCode()方法,并且接口中并未定義該方法,就使用自動(dòng)生成的hashCode()方法
            return hashCode();
        } else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // 如果當(dāng)前方法是Spring織入的DecoratingProxy接口中的方法,則返回目標(biāo)對(duì)象的Class類型
            return AopProxyUtils.ultimateTargetClass(this.advised);
        } else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 如果當(dāng)前方法是Spring織入的Advised接口中的方法,
            // 則使用反射調(diào)用當(dāng)前advised對(duì)象中的相關(guān)方法
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }

        Object retVal;
        if (this.advised.exposeProxy) {
            // 如果設(shè)置了需要暴露代理對(duì)象,則將當(dāng)前對(duì)象設(shè)置到AopContext中
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }

        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);

        // 獲取當(dāng)前方法需要織入的切面邏輯的調(diào)用鏈
        List<Object> chain = this.advised
            .getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

        if (chain.isEmpty()) {
            // 如果切面邏輯的調(diào)用鏈為空,則對(duì)方法參數(shù)進(jìn)行類型轉(zhuǎn)換處理,
            // 并且通過反射直接調(diào)用目標(biāo)對(duì)象的方法
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
        } else {
            // 獲取目標(biāo)對(duì)象的調(diào)用鏈邏輯,并且對(duì)該鏈進(jìn)行調(diào)用
            invocation = new ReflectiveMethodInvocation(proxy,
                target, method, args, targetClass, chain);
            retVal = invocation.proceed();
        }

        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target &&
            returnType != Object.class && returnType.isInstance(proxy) &&
            !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // 判斷返回值如果為目標(biāo)對(duì)象,并且當(dāng)前方法的返回值類型是當(dāng)前代理對(duì)象的類型,那么就將
            // 當(dāng)前代理對(duì)象返回。這里的邏輯的實(shí)際意思簡(jiǎn)單的說就是,如果返回值是目標(biāo)對(duì)象,那么
            // 就將當(dāng)前代理對(duì)象返回
            retVal = proxy;
        } else if (retVal == null && returnType != Void.TYPE 
                   && returnType.isPrimitive()) {
            // 如果返回值滿足其為空,不是Void類型,并且是基本數(shù)據(jù)類型,那么就拋出異常,
            // 因?yàn)榛緮?shù)據(jù)類型的返回值必然不為空
            throw new AopInvocationException("Null return value from advice does not " 
                + " match primitive return type for: " + method);
        }
        return retVal;
    } finally {
        if (target != null && !targetSource.isStatic()) {
            // 如果TargetSource不是靜態(tài)的,則調(diào)用其releaseTarget()方法將生成的目標(biāo)對(duì)象釋放
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 處理AopContext中保存的當(dāng)前代理對(duì)象
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

可以看到,在進(jìn)行代理邏輯的織入的時(shí)候,首先會(huì)進(jìn)行如下判斷:
①當(dāng)前方法是否為equals()或hashCode()方法,如果是,并且接口中并未要求子類實(shí)現(xiàn)這些方法,那么就會(huì)調(diào)用自動(dòng)生成的方法;
②當(dāng)前方法是否為Spring織入的DecoratingProxy接口中的方法,如果是,則將目標(biāo)對(duì)象的Class類型返回;
③判斷目標(biāo)方法是否為Spring織入的Advised中的方法,如果是,則調(diào)用當(dāng)前advised對(duì)象中相應(yīng)的方法。然后就會(huì)獲取當(dāng)前方法需要織入的代理邏輯的調(diào)用鏈。接著就會(huì)將目標(biāo)對(duì)象和調(diào)用鏈邏輯封裝為ReflectiveMethodInvocation,并進(jìn)行調(diào)用。最后對(duì)調(diào)用的返回值進(jìn)行一些基本判斷,并且返回。

3. 小結(jié)

本文首先對(duì)Jdk代理進(jìn)行了簡(jiǎn)單的介紹,然后介紹了Spring Aop是如何調(diào)用將Jdk代理的Proxy引入并且生成代理對(duì)象的,最后對(duì)調(diào)用鏈的生成,以及切面邏輯的織入方式進(jìn)行了講解。關(guān)于調(diào)用鏈的生成以及切面邏輯的織入,由于使用的方式和Cglib是一樣的,因而這里并沒有對(duì)其細(xì)節(jié)進(jìn)行過多描述。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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