1. 程式人生 > >深入原始碼解析spring aop實現的三個過程

深入原始碼解析spring aop實現的三個過程

Spring AOP的面向切面程式設計,是面向物件程式設計的一種補充,用於處理系統中分佈的各個模組的橫切關注點,比如說事務管理、日誌、快取等。它是使用動態代理實現的,在記憶體中臨時為方法生成一個AOP物件,這個物件包含目標物件的所有方法,在特定的切點做了增強處理,並回調原來的方法。

Spring AOP的動態代理主要有兩種方式實現,JDK動態代理和cglib動態代理。JDK動態代理通過反射來接收被代理的類,但是被代理的類必須實現介面,核心是InvocationHandler和Proxy類。cglib動態代理的類一般是沒有實現介面的類,cglib是一個程式碼生成的類庫,可以在執行時動態生成某個類的子類,所以,CGLIB是通過繼承的方式做的動態代理,因此如果某個類被標記為final,那麼它是無法使用CGLIB做動態代理的。

AOP實現中,可以看到三個主要的步驟,一個是代理物件的生成,然後是攔截器的作用,然後是Aspect編織的實現。

ProxyFactoryBean生成AopProxy

 

ProxyFactoryBean生成AOP proxy

 1     /**
 2      * Return a proxy. Invoked when clients obtain beans from this factory bean.
 3      * Create an instance of the AOP proxy to be returned by this factory.
 4      * The instance will be cached for a singleton, and create on each call to
 5      * <code>getObject()</code> for a proxy.
 6      * @return a fresh AOP proxy reflecting the current state of this factory
 7      */
 8     public Object getObject() throws BeansException {
 9         initializeAdvisorChain();
10         if (isSingleton()) {
11             return getSingletonInstance();
12         }
13         else {
14             if (this.targetName == null) {
15                 logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
16                         "Enable prototype proxies by setting the 'targetName' property.");
17             }
18             return newPrototypeInstance();
19         }
20     }

 

初始化Advisor chain

 1     /**
 2      * Create the advisor (interceptor) chain. Aadvisors that are sourced
 3      * from a BeanFactory will be refreshed each time a new prototype instance
 4      * is added. Interceptors added programmatically through the factory API
 5      * are unaffected by such changes.
 6      */
 7     private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
 8         if (this.advisorChainInitialized) {
 9             return;
10         }
11 
12         if (!ObjectUtils.isEmpty(this.interceptorNames)) {
13             if (this.beanFactory == null) {
14                 throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
15                         "- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
16             }
17 
18             // Globals can't be last unless we specified a targetSource using the property...
19             if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
20                     this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
21                 throw new AopConfigException("Target required after globals");
22             }
23 
24             // Materialize interceptor chain from bean names.
25             for (int i = 0; i < this.interceptorNames.length; i++) {
26                 String name = this.interceptorNames[i];
27                 if (logger.isTraceEnabled()) {
28                     logger.trace("Configuring advisor or advice '" + name + "'");
29                 }
30 
31                 if (name.endsWith(GLOBAL_SUFFIX)) {
32                     if (!(this.beanFactory instanceof ListableBeanFactory)) {
33                         throw new AopConfigException(
34                                 "Can only use global advisors or interceptors with a ListableBeanFactory");
35                     }
36                     addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
37                             name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
38                 }
39 
40                 else {
41                     // If we get here, we need to add a named interceptor.
42                     // We must check if it's a singleton or prototype.
43                     Object advice = null;
44                     if (this.singleton || this.beanFactory.isSingleton(this.interceptorNames[i])) {
45                         // Add the real Advisor/Advice to the chain.
46                         advice = this.beanFactory.getBean(this.interceptorNames[i]);
47                     }
48                     else {
49                         // It's a prototype Advice or Advisor: replace with a prototype.
50                         // Avoid unnecessary creation of prototype bean just for advisor chain initialization.
51                         advice = new PrototypePlaceholderAdvisor(this.interceptorNames[i]);
52                     }
53                     addAdvisorOnChainCreation(advice, this.interceptorNames[i]);
54                 }
55             }
56         }
57 
58         this.advisorChainInitialized = true;
59     }

 

增加advisor chain(AdvisedSupport.java)

 1     private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
 2         Assert.notNull(advisor, "Advisor must not be null");
 3         if (isFrozen()) {
 4             throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
 5         }
 6         if (pos > this.advisors.size()) {
 7             throw new IllegalArgumentException(
 8                     "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
 9         }
10         this.advisors.add(pos, advisor);
11         updateAdvisorArray();
12         adviceChanged();
13     }

 

Spring AOP中攔截器鏈

1.開始步驟--獲取AopProxy主流程

ProxyCreatorSupport.java

    /**
     * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
     * create an AOP proxy with <code>this</code> as an argument.
     */
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

2.獲取AopProxy實現 --DefaultAopProxyFactory.java

ProxyFactoryBean類繼承了AdvisedSupport類,後者繼承了ProxyConfig類並定義了操作advisor 和interceptor的介面,以支援AOP。當BeanFactory例項化ProxyFactoryBean時,根據配置檔案的定義將關於 advice,pointcut,advisor,所代理的介面和介面實現類的所有資訊傳給ProxyFactoryBean。

當客戶程式呼叫BeanFactory的getBean方法時,ProxyFactory使用JdkDynamicAopProxy例項化 BeanImpl類,並用JdkDynamicAopProxy的invoke方法執行advice。至於執行advice的時機,由 ProxyFactoryBean呼叫RegexpMethodPointcutAdvisor進行判斷。

    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }
            if (targetClass.isInterface()) {
                return new JdkDynamicAopProxy(config);
            }
            if (!cglibAvailable) {
                throw new AopConfigException(
                        "Cannot proxy target class because CGLIB2 is not available. " +
                        "Add CGLIB to the class path or specify proxy interfaces.");
            }
            return CglibProxyFactory.createCglibProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }

 

3.獲取AopProxy的執行路徑

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

 

4.激發攔截器鏈主過程

 1     /**
 2      * Implementation of <code>InvocationHandler.invoke</code>.
 3      * <p>Callers will see exactly the exception thrown by the target,
 4      * unless a hook method throws an exception.
 5      */
 6     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 7         MethodInvocation invocation = null;
 8         Object oldProxy = null;
 9         boolean setProxyContext = false;
10 
11         TargetSource targetSource = this.advised.targetSource;
12         Class targetClass = null;
13         Object target = null;
14 
15         try {
16             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
17                 // The target does not implement the equals(Object) method itself.
18                 return (equals(args[0]) ? Boolean.TRUE : Boolean.FALSE);
19             }
20             if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
21                 // The target does not implement the hashCode() method itself.
22                 return new Integer(hashCode());
23             }
24             if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
25                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
26                 // Service invocations on ProxyConfig with the proxy config...
27                 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
28             }
29 
30             Object retVal = null;
31 
32             if (this.advised.exposeProxy) {
33                 // Make invocation available if necessary.
34                 oldProxy = AopContext.setCurrentProxy(proxy);
35                 setProxyContext = true;
36             }
37 
38             // May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
39             // in case it comes from a pool.
40             target = targetSource.getTarget();
41             if (target != null) {
42                 targetClass = target.getClass();
43             }
44 
45             // Get the interception chain for this method.
46             List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
47 
48             // Check whether we have any advice. If we don't, we can fallback on direct
49             // reflective invocation of the target, and avoid creating a MethodInvocation.
50             if (chain.isEmpty()) {
51                 // We can skip creating a MethodInvocation: just invoke the target directly
52                 // Note that the final invoker must be an InvokerInterceptor so we know it does
53                 // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
54                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
55             }
56             else {
57                 // We need to create a method invocation...
58                 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
59                 // Proceed to the joinpoint through the interceptor chain.
60                 retVal = invocation.proceed();
61             }
62 
63             // Massage return value if necessary.
64             if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
65                     !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
66                 // Special case: it returned "this" and the return type of the method
67                 // is type-compatible. Note that we can't help if the target sets
68                 // a reference to itself in another returned object.
69                 retVal = proxy;
70             }
71             return retVal;
72         }
73         finally {
74             if (target != null && !targetSource.isStatic()) {
75                 // Must have come from TargetSource.
76                 targetSource.releaseTarget(target);
77             }
78             if (setProxyContext) {
79                 // Restore old proxy.
80                 AopContext.setCurrentProxy(oldProxy);
81             }
82         }
83     }

 

5.獲取攔截器鏈DefaultAdvisorChainFactory.java

    public List 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 interceptorList = new ArrayList(config.getAdvisors().length);
        boolean hasIntroductions = hasMatchingIntroductions(config, targetClass);
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        Advisor[] advisors = config.getAdvisors();
        for (int i = 0; i < advisors.length; i++) {
            Advisor advisor = advisors[i];
            if (advisor instanceof PointcutAdvisor) {
                // Add it conditionally.
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    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 (int j = 0; j < interceptors.length; j++) {
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j], 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;
    }
6.激發攔截鏈工作實現 ---ReflectiveMethodInvocation.java

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

 

Spring AOP中Aspect編織的實現

1.前面我們談到攔截器起作用時,實現程式碼(ReflectiveMethodInvocation.java)如下:

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

 

2.前置Advice MethodBeforeAdviceInterceptor.java

    public Object invoke(MethodInvocation mi) throws Throwable {
        this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
        return mi.proceed();
    }

 

3.後置Advice AfterReturningAdviceInterceptor.java

    public Object invoke(MethodInvocation mi) throws Throwable {
        Object retVal = mi.proceed();
        this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
        return retVal;
    }

 

總結

沒圖沒真相

 

 Spring框架的AOP機制可以讓開發者把業務流程中的通用功能抽取出來,單獨編寫功能程式碼。在業務流程執行過程中,Spring框架會根據業務流程要求,自動把獨立編寫的功能程式碼切入到流程的合適位置。

&nbs