AOP源碼分析-CglibAopProxy DynamicAdvisedInterceptor,springaop源碼分析

來源:互聯網
上載者:User

AOP源碼分析-CglibAopProxy DynamicAdvisedInterceptor,springaop源碼分析

最近新公司在用Spring MVC,跟蹤Spring的Service發現是通過動態代理來實現的,而公司的事務是配置在Service層。所以想看下Spring 的AOP的具體實現。本文源碼基於Spring 4.0。

我們可以使用debug跟蹤一次Service調用的整體流程,可以清晰的看到一次流程處理:
CglibAopProxy.intercept方法,該方法中通過

 this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

擷取一個List攔截鏈,然後通過

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();

方法執行對應的攔截鏈進行執行處理。
最後通過,

 processReturnType(proxy, target, method, retVal);

處理傳回值,並返回。
整體過程完全採用動態代理模式來實現。最主要的代碼如下:

@Override        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {            Object oldProxy = null;            boolean setProxyContext = false;            Class<?> targetClass = null;            Object target = null;            try {                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 = getTarget();                if (target != null) {                    targetClass = target.getClass();                }                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);                Object retVal;                // Check whether we only have one InvokerInterceptor: that is,                // no real advice, but just reflective invocation of the target.                if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {                    // 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 = methodProxy.invoke(target, args);                }                else {                    // We need to create a method invocation...                    retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();                }                retVal = processReturnType(proxy, target, method, retVal);                return retVal;            }            finally {                if (target != null) {                    releaseTarget(target);                }                if (setProxyContext) {                    // Restore old proxy.                    AopContext.setCurrentProxy(oldProxy);                }            }        }

代碼看起來很簡單。
下面我們可以逐行對代碼進行分析。
方法簽名:

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable

可以看出,改方法傳入代理對象,方法對象,參數以及方法代理。MethodProxy 包含我們的需要被代理的方法資訊,包含方法完整簽名。

if (this.advised.exposeProxy) {                    // Make invocation available if necessary.                    oldProxy = AopContext.setCurrentProxy(proxy);                    setProxyContext = true;                }

此處代碼是判斷代理是否可用,大概是這個意思,此處的代碼不是很明白。

target = getTarget();                if (target != null) {                    targetClass = target.getClass();                }

此處是擷取目標類對象。
下來是擷取對應的鏈對象。

List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);    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;    }

在這個方法中,比較重要的應該是cacheKey的擷取。

/**     * Simple wrapper class around a Method. Used as the key when     * caching methods, for efficient equals and hashCode comparisons.     */    private static class MethodCacheKey {        private final Method method;        private final int hashCode;        public MethodCacheKey(Method method) {            this.method = method;            this.hashCode = method.hashCode();        }        @Override        public boolean equals(Object other) {            if (other == this) {                return true;            }            MethodCacheKey otherKey = (MethodCacheKey) other;            return (this.method == otherKey.method);        }        @Override        public int hashCode() {            return this.hashCode;        }    }}

這個類比較簡單,定義了對應的Method對象,以及Method對應的hashCode。

List<Object> cached = this.methodCache.get(cacheKey);    /** Cache with Method as key and advisor chain List as value */    private transient Map<MethodCacheKey, List<Object>> methodCache;

此處緩衝了對應的攔截器鏈。如果為null,則通過factory執行個體化,並放入緩衝。

if (cached == null) {            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(                    this, method, targetClass);            this.methodCache.put(cacheKey, cached);        }@Override    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);        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) {                // Add it conditionally.                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()) {                            // 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(actualClass)) {                    Interceptor[] interceptors = registry.getInterceptors(advisor);                    interceptorList.addAll(Arrays.asList(interceptors));                }            }            else {                Interceptor[] interceptors = registry.getInterceptors(advisor);                interceptorList.addAll(Arrays.asList(interceptors));            }        }        return interceptorList;    }

這個方法雖然很長,但是實際內容還是相對比較簡單。
首先擷取所有切點。

config.getAdvisors()

判斷不同的切點類型,放入List。
執行對應List方法。

retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();@Override    public Object proceed() throws Throwable {        //  We start with an index of -1 and increment early.        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {            return invokeJoinpoint();        }        Object interceptorOrInterceptionAdvice =                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {            // Evaluate dynamic method matcher here: static part will already have            // been evaluated and found to match.            InterceptorAndDynamicMethodMatcher dm =                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;            if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {                return dm.interceptor.invoke(this);            }            else {                // Dynamic matching failed.                // Skip this interceptor and invoke the next in the chain.                return proceed();            }        }        else {            // It's an interceptor, so we just invoke it: The pointcut will have            // been evaluated statically before this object was constructed.            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);        }    }

處理傳回值:

retVal = processReturnType(proxy, target, method, retVal);                return retVal;

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.