Spring系列之AOP(4)——JdkDynamicAopProxy與CglibAopProxy

1置蜀、JdkDynamicAopProxy

前文提到過,在JDK代理中方法回掉的入口是在invoke方法中悉盆。而JdkDynamicAopProxy實現(xiàn)了InvocationHandler接口盯荤,方法回掉邏輯也定義在其中.

(1)、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;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // 目標沒有實現(xiàn)自己的equals方法
            return equals(args[0]);
        }
        if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // 目標沒有實現(xiàn)自己的hashCodes方法
            return hashCode();
        }
        if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                method.getDeclaringClass().isAssignableFrom(Advised.class)) {
            // 根據(jù)代理對象的配置調(diào)用服務焕盟,如果是Advised接口的實現(xiàn)類秋秤,則直接調(diào)用
            return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
        }
  
        Object retVal;
  
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
  
        // 有可能為null.盡可能減少擁有目標對象的時間,在這種情況下對象來自于對象池
        target = targetSource.getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
  
        // 獲得這個方法的攔截器鏈
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);//見2
        //如果沒有攔截器鏈脚翘,則直接調(diào)用目標對象
        if (chain.isEmpty()) {
            retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
        }
        else {
            // 構(gòu)造一個方法調(diào)用
            invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // 調(diào)用連接點的攔截器鏈(見3)
            retVal = invocation.proceed();
        }
  
        Class<?> returnType = method.getReturnType();
        if (retVal != null && retVal == target && returnType.isInstance(proxy) &&
                !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            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()) {
            // 必須來自TargetSource.
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            // 重新保存舊的代理
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

(2)灼卢、 獲得方法的攔截器鏈

//JdkDynamicAopProxy類
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;
}

methodCache是一個集合Map<MethodCacheKey, List<Object>>,對方法的攔截器鏈進行緩存,如果不在緩存中来农,則生成并添加鞋真。這里使用DefaultAdvisorChainFactory來生成攔截器鏈

//DefaultAdvisorChainFactory類
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
        Advised config, Method method, Class<?> targetClass) {
 
    //通過config獲得配置好的advisor鏈,AdvisedSupport實現(xiàn)了Advised
    List<Object> interceptorList = new ArrayList<Object>(config.getAdvisors().length);
    //實際對象
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    //判斷是否符合配置要求
    boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
 
    for (Advisor advisor : config.getAdvisors()) {
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            //判斷通知器是否匹配實際對象
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                MethodInterceptor[] interceptors = registry.getInterceptors(advisor);//將通知器適配成方法攔截
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                    if (mm.isRuntime()) {
                        for (MethodInterceptor interceptor : interceptors) {
                            //封裝成動態(tài)方法匹配
                            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(actualClass)) {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        else {
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }
    return interceptorList;
}

此方法有一個適配和注冊的過程沃于,它將Advice通知適配成Spring預先設計好的攔截器涩咖。適配和注冊的工作是在GlobalAdvisorAdapterRegistry的getInterceptors()中完成的

//DefaultAdvisorAdapterRegistry類
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
    List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
    Advice advice = advisor.getAdvice();//獲得通知
    if (advice instanceof MethodInterceptor) {//如果是MethodInterceptor則直接添加
        interceptors.add((MethodInterceptor) advice);
    }
    for (AdvisorAdapter adapter : this.adapters) {//遍歷注冊的適配器海诲,檢查是否匹配
        if (adapter.supportsAdvice(advice)) {
            interceptors.add(adapter.getInterceptor(advisor));
        }
    }
    if (interceptors.isEmpty()) {
        throw new UnknownAdviceTypeException(advisor.getAdvice());
    }
    return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
}

在DefaultAdvisorAdapterRegistry的構(gòu)造函數(shù)中注冊了三種適配器,注冊過程就是將這三種適配器加入List集合

public DefaultAdvisorAdapterRegistry() {
    registerAdvisorAdapter(newMethodBeforeAdviceAdapter());
    registerAdvisorAdapter(newAfterReturningAdviceAdapter());
    registerAdvisorAdapter(newThrowsAdviceAdapter());
}

來看一下MethodBeforeAdviceAdapter

class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
    @Override
    public boolean supportsAdvice(Advice advice) {//是否支持
        return (advice instanceof MethodBeforeAdvice);
    }
    @Override
    public MethodInterceptor getInterceptor(Advisor advisor) {//將Advice適配成Interceptor
        MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
        return new MethodBeforeAdviceInterceptor(advice);
    }
}
將Advice封裝成了MethodBeforeAdviceInterceptor,此類中有invoke方法檩互,會先調(diào)用advice的before方法
publicObject invoke(MethodInvocation mi)throws Throwable {
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
    return mi.proceed();//見下
}

至此特幔,Spring AOP實現(xiàn)了對advice的織入,可以看到它將xml中配置的通知器適配成了攔截器

(3)盾似、 方法調(diào)用

之前講到了攔截器的適配和注冊敬辣,對呀?jīng)]有攔截器的方法直接調(diào)用,有攔截器的方法會構(gòu)造ReflectiveMethodInvocation零院,并沿著攔截器鏈進行調(diào)用溉跃。整個調(diào)用鏈的入口在proceed方法中

//ReflectiveMethodInvocation類
public Object proceed() throws Throwable {
    //從索引為-1的攔截器開始,并遞增
    //如果攔截器迭代調(diào)用完成告抄,則調(diào)用目標方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();//使用invokeJoinpointUsingReflection調(diào)用目標對象
    }
    //沿著攔截器鏈執(zhí)行
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        //對方法進行動態(tài)匹配撰茎,切點的匹配就在這里進行
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        }
        else {
            // 動態(tài)匹配失敗
            // 跳過這個攔截器調(diào)用下一個
            return proceed();
        }
    }
    else {
        // 這是一個攔截器,直接調(diào)用它
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

至此打洼,完成對攔截器鏈及目標方法的調(diào)用

2龄糊、CglibAopProxy

//CglibAopProxy類
public Object getProxy(ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
 
        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
 
            Class<?> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }
 
            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass);
 
            // Configure CGLIB Enhancer...
            Enhancer enhancer = createEnhancer();
            if (classLoader != null) {
                enhancer.setClassLoader(classLoader);
                if (classLoader instanceof SmartClassLoader &&
                        ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                    enhancer.setUseCache(false);
                }
            }
            enhancer.setSuperclass(proxySuperClass);
            enhancer.setStrategy(new MemorySafeUndeclaredThrowableStrategy(UndeclaredThrowableException.class));
            enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
            enhancer.setInterceptDuringConstruction(false);
 
            Callback[] callbacks = getCallbacks(rootClass);
            enhancer.setCallbacks(callbacks);
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
 
            Class<?>[] types = new Class[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            enhancer.setCallbackTypes(types);
 
            // Generate the proxy class and create a proxy instance.
            Object proxy;
            if (this.constructorArgs != null) {
                proxy = enhancer.create(this.constructorArgTypes, this.constructorArgs);
            }
            else {
                proxy = enhancer.create();
            }
 
            return proxy;
        }
        catch (CodeGenerationException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Exception ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

重點是之前getCallbacks方法,進行織入

//CglibAopProxy類
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
        // Parameters used for optimisation choices...
        boolean exposeProxy = this.advised.isExposeProxy();
        boolean isFrozen = this.advised.isFrozen();
        boolean isStatic = this.advised.getTargetSource().isStatic();
 
        // Choose an "aop" interceptor (used for AOP calls).
        Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
 
        // Choose a "straight to target" interceptor. (used for calls that are
        // unadvised but can return this). May be required to expose the proxy.
        Callback targetInterceptor;
        if (exposeProxy) {
            targetInterceptor = isStatic ?
                    new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
                    new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
        }
        else {
            targetInterceptor = isStatic ?
                    new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
                    new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
        }
 
        // Choose a "direct to target" dispatcher (used for
        // unadvised calls to static targets that cannot return this).
        Callback targetDispatcher = isStatic ?
                new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp();
 
        Callback[] mainCallbacks = new Callback[]{
            aopInterceptor, // for normal advice
            targetInterceptor, // invoke target without considering advice, if optimized
            new SerializableNoOp(), // no override for methods mapped to this
            targetDispatcher, this.advisedDispatcher,
            new EqualsInterceptor(this.advised),
            new HashCodeInterceptor(this.advised)
        };
 
        Callback[] callbacks;
 
        // If the target is a static one and the advice chain is frozen,
        // then we can make some optimisations by sending the AOP calls
        // direct to the target using the fixed chain for that method.
        if (isStatic && isFrozen) {
            Method[] methods = rootClass.getMethods();
            Callback[] fixedCallbacks = new Callback[methods.length];
            this.fixedInterceptorMap = new HashMap<String, Integer>(methods.length);
 
            // TODO: small memory optimisation here (can skip creation for
            // methods with no advice)
            for (int x = 0; x < methods.length; x++) {
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
                fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
                        chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
                this.fixedInterceptorMap.put(methods[x].toString(), x);
            }
 
            // Now copy both the callbacks from mainCallbacks
            // and fixedCallbacks into the callbacks array.
            callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
            System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
            System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
            this.fixedInterceptorOffset = mainCallbacks.length;
        }
        else {
            callbacks = mainCallbacks;
        }
        return callbacks;
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末募疮,一起剝皮案震驚了整個濱河市炫惩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌阿浓,老刑警劉巖他嚷,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芭毙,居然都是意外死亡筋蓖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門退敦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粘咖,“玉大人,你說我怎么就攤上這事侈百∥拖拢” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵钝域,是天一觀的道長讽坏。 經(jīng)常有香客問我,道長网梢,這世上最難降的妖魔是什么震缭? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任赂毯,我火速辦了婚禮战虏,結(jié)果婚禮上拣宰,老公的妹妹穿的比我還像新娘。我一直安慰自己烦感,他們只是感情好巡社,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著手趣,像睡著了一般晌该。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绿渣,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天朝群,我揣著相機與錄音,去河邊找鬼中符。 笑死姜胖,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的淀散。 我是一名探鬼主播右莱,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼档插!你這毒婦竟也來了慢蜓?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤郭膛,失蹤者是張志新(化名)和其女友劉穎晨抡,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體饲鄙,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡凄诞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了忍级。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帆谍。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖轴咱,靈堂內(nèi)的尸體忽然破棺而出汛蝙,到底是詐尸還是另有隱情,我是刑警寧澤朴肺,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布窖剑,位于F島的核電站,受9級特大地震影響戈稿,放射性物質(zhì)發(fā)生泄漏西土。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一鞍盗、第九天 我趴在偏房一處隱蔽的房頂上張望需了。 院中可真熱鬧跳昼,春花似錦、人聲如沸肋乍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽墓造。三九已至堪伍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間觅闽,已是汗流浹背帝雇。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛉拙,地道東北人摊求。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像刘离,于是被迫代替她去往敵國和親室叉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 前文介紹了代理模式及實現(xiàn)硫惕,但想要實現(xiàn)一個完整的AOP框架還遠遠不夠茧痕,接下來我們來分析一下Spring是如何實現(xiàn)AO...
    MagicWolf閱讀 2,689評論 1 18
  • title: Spring_AOP源碼分析date: 2016-11-03 01:15:11categories:...
    raincoffee閱讀 1,731評論 2 36
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)恼除,斷路器踪旷,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 四,Spring AOP攔截器調(diào)用的實現(xiàn) 1. 設計原理 在Spring AOP通過JDK的Proxy方式或者CG...
    WhyNotYue閱讀 1,827評論 5 4
  • 穿衣豁辉,時尚達人告訴你令野,全身上下不要超過三個顏色,Channel以一條優(yōu)雅的小黑裙彰顯風格徽级,Christia...
    Silvia花花閱讀 266評論 2 3