AOP source code analysis-CglibAopProxy DynamicAdvisedInterceptor, springaop source code analysis

Source: Internet
Author: User

AOP source code analysis-CglibAopProxy DynamicAdvisedInterceptor, springaop source code analysis

Recently, new companies are using Spring MVC to track Spring Service discovery through dynamic proxy, and the company's transactions are configured on the Service layer. So I want to see the specific implementation of Spring AOP. The source code of this article is based on Spring 4.0.

We can use debug to track the overall process of a Service call and clearly view the process:
CglibAopProxy. intercept Method.

 this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

Obtain a List interception chain, and then use

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

Method execution corresponding to the interception chain for execution.
Finally,

 processReturnType(proxy, target, method, retVal);

Process the returned value and return it.
The overall process is fully implemented using the dynamic proxy mode. The main code is as follows:

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

The code looks simple.
Next we can analyze the code line by line.
Method signature:

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

It can be seen that the modification method is passed into the proxy object, method object, parameters, and method proxy. MethodProxy contains information about the methods to be proxy, including the complete method signature.

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

The code here is used to determine whether the proxy is available, which is probably the meaning. The code here is not very clear.

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

Obtain the target class object.
Obtain the corresponding link object.

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

In this method, it is important to obtain the 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;        }    }}

This class is relatively simple. It defines the corresponding Method object and the hashCode corresponding to the Method.

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;

The corresponding interceptor chain is cached here. If it is null, It is instantiated through the factory and put into the cache.

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

This method is very long, but the actual content is relatively simple.
First, obtain all the cut points.

config.getAdvisors()

Determine different shard types and add them to the List.
Execute the corresponding List method.

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

Processing return value:

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.