1. 程式人生 > >spring——AOP原理及原始碼(四)

spring——AOP原理及原始碼(四)

前情回顧:

  上文我們一路分析了從容器建立開始直到我們的AOP註解匯入的核心元件AnnotationAwareAspectJAutoProxyCreator執行postProcessBeforeInstantiation()方法的整個過程

  分析得到:在所有bean建立之前,都會呼叫resolveBeforeInstantiation方法來試圖返回一個代理物件


 

本篇預告

下圖可以看到resolveBeforeInstantiation方法包含了

applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);

applyBeanPostProcessorsAfterInitialization(bean, beanName);

兩個方法

 

在本篇我們將完整走完 resolveBeforeInstantiation 全過程,並一直到返回代理物件為止


 

 

除錯的起點

  開始除錯,還是一路跳到下一斷點,直到AbstractAutoProxyCreator.postProcessBeforeInstantiation()(從resolveBeforeInstantiation方法進入到這裡的過程上一篇已經分析了)

 

可以看到當前的bean為org.springframework.context.event.internalEventListenerProcessor,和我們要測試的AOP無關。

因為當前方法打上了斷點,所以我們調到下一個斷點直到來到class aop.MathCalculator

 1 @Override
 2     public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
 3         Object cacheKey = getCacheKey(beanClass, beanName);
 4 
 5         if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
 6             if (this.advisedBeans.containsKey(cacheKey)) {
 7                 return null;
 8             }
 9             if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
10                 this.advisedBeans.put(cacheKey, Boolean.FALSE);
11                 return null;
12             }
13         }
14 
15         // Create proxy here if we have a custom TargetSource.
16         // Suppresses unnecessary default instantiation of the target bean:
17         // The TargetSource will handle target instances in a custom fashion.
18         if (beanName != null) {
19             TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
20             if (targetSource != null) {
21                 this.targetSourcedBeans.add(beanName);
22                 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
23                 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
24                 this.proxyTypes.put(cacheKey, proxy.getClass());
25                 return proxy;
26             }
27         }
28 
29         return null;
30     }
postProcessBeforeInstantiation

 

從上往下一步步step voer,下面是對應行的講解

3、獲取bean在容器中的快取

5、判斷目標源Bean中是否存在有當前bean的快取資訊。

(可以在21行看到新增目標源bean的操作,在23行就建立了代理物件。所以這步也是相當於判斷當前bean是否已經建立過代理物件。)

  因為是第一次執行MathCalculator這個bean,這裡我們是進入判斷的

  接下來 this.advisedBeans.containsKey(cacheKey) 判斷advisedBeans中是否有快取(這裡我們將advisedBeans稱為增強器)

  我們這裡判斷是不滿足的,接著往下走。

9、進行 isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName) 判斷

  下面分別進入第九行的兩個方法進行檢視。


 

  一、isInfrastructureClass(beanClass) 是否是基礎型別

    進入後我們會發現有如下兩個方法

    super.isInfrastructureClass(beanClass) || this.aspectJAdvisorFactory.isAspect(beanClass)

  

 

  先進入父類的 isInfrastructureClass 方法,經過一系列判斷,最後返回false,表明當前bean不是基礎型別。

 

   

   

  接著來到 isAspect(beanClass) ,通過類上是否有@Aspect註解來判斷這個類是否是切面(這裡 MathCalculator顯然不是一個切面

  

 

   返回出來可以看到 isInfrastructureClass(beanClass) 的判斷為false

  

  

  二、shouldSkip 判斷是否要跳過

  

 

  在方法中遍歷所有的增強器,紅框中表面獲取的增強器便是日誌方法。  

  並判斷增強器是否是AspectJPointcutAdvisor型別,我們這裡判斷不滿足

  末尾來到父類的  shouldSkip 方法,進入可以看到直接返回了 false

  

 

   

最終我們來到外層的判斷,可以看到返回了false


 

退出 applyBeanPostProcessorsBeforeInstantiation 方法

接著來到下面這塊程式碼,看註釋表明如果存在自定義目標Source,我們將在此建立代理物件

step voer,在259行判斷targetSource為null,所以這裡是沒有自定義目標Source的

我們一直往下走,走完 applyBeanPostProcessorsBeforeInstantiation 方法,直到回到 resolveBeforeInstantiation 方法,返回的bean為null

所以接下來也不會進入 applyBeanPostProcessorsAfterInitialization 方法

 

到此為止,我們的 resolveBeforeInstantiation 方法執行完了,從以上可以得知,方法沒有給我們返回代理物件

如下圖所示,我們將接著執行 createBean 流程,接下來將呼叫 doCreateBean 


 

postProcessAfterInitialization方法探究

我們執行 doCreateBean 方法,來到了配置類的bean方法

 

接著跳到下一個斷點直到 postProcessAfterInitialization 方法,下面的方法棧我們是熟悉的

從finishBeanFactoryInitialization一路到initializeBean

 

不過我們現在進入的是postProcessAfterInitialization 

從下圖的 initializeBean 方法的流程也可以看明白

前面執行完了 applyBeanPostProcessorsBeforeInitialization 和 invokeInitMethods 兩個方法

 

下面進入 postProcessAfterInitialization 方法:

  如果先前的代理參考中不存在當前bean物件,就呼叫 wrapIfNecessary(bean, beanName, cacheKey) 並返回其結果

  

 

   進入wrapIfNecessary(進行包裝如果需要的話):

 1 /**
 2      * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 3      * @param bean the raw bean instance
 4      * @param beanName the name of the bean
 5      * @param cacheKey the cache key for metadata access
 6      * @return a proxy wrapping the bean, or the raw bean instance as-is
 7      */
 8     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
 9         if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
10             return bean;
11         }
12         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
13             return bean;
14         }
15         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
16             this.advisedBeans.put(cacheKey, Boolean.FALSE);
17             return bean;
18         }
19 
20         // Create proxy if we have advice.
21         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
22         if (specificInterceptors != DO_NOT_PROXY) {
23             this.advisedBeans.put(cacheKey, Boolean.TRUE);
24             Object proxy = createProxy(
25                     bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
26             this.proxyTypes.put(cacheKey, proxy.getClass());
27             return proxy;
28         }
29 
30         this.advisedBeans.put(cacheKey, Boolean.FALSE);
31         return bean;
32     }
wrapIfNecessary

 

9~18行:還是先進行判斷,是否是基礎型別等,在這裡我們判斷都不是

21:拿到增強器放入 Object[] specificInterceptors 中

24~25:傳入拿到的增強器等資訊,建立代理物件

下面我們著重來看 getAdvicesAndAdvisorsForBean 拿增強器的過程:

  一進來先呼叫findEligibleAdvisors,找到合格的增強器(日誌方法)

  

 

   進入findEligibleAdvisors,方法先呼叫findCandidateAdvisors,獲取所有候選增強器

  然後又呼叫findAdvisoersThatCanApply從候選增強器中選出可以用於當前bean的

  接著判斷選出的增強器佇列不為空,就給其排序,最後返回選出的增強器佇列

   

   

   findCandidateAdvisors 我們就不說了

  重點看看 findAdvisoersThatCanApply

  如下圖,可以發現它是利用AopUtils,也就是AOP工具類進行篩選

   

 

   進入工具類方法,看到方法又進行了一層判斷,將最終符合條件的增強器(日誌方法)放入選擇佇列中

   

 

   獲取完後我們一路返回,又回到了 findEligibleAdvisors

  如下圖可以看到,最終排序完後將返回有五個增強器的增強器佇列

 


 

 

建立代理物件

最終我們獲取到了需要的增強器(日誌方法)放入一個叫特殊攔截器的陣列(這裡暫且稱為攔截器陣列)

判斷不為空後,將當前bean的快取放入adviseBeans 中

接著呼叫createProxy來建立代理物件

 

 

 

 1 /**
 2      * Create an AOP proxy for the given bean.
 3      * @param beanClass the class of the bean
 4      * @param beanName the name of the bean
 5      * @param specificInterceptors the set of interceptors that is
 6      * specific to this bean (may be empty, but not null)
 7      * @param targetSource the TargetSource for the proxy,
 8      * already pre-configured to access the bean
 9      * @return the AOP proxy for the bean
10      * @see #buildAdvisors
11      */
12     protected Object createProxy(
13             Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
14 
15         if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
16             AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
17         }
18 
19         ProxyFactory proxyFactory = new ProxyFactory();
20         proxyFactory.copyFrom(this);
21 
22         if (!proxyFactory.isProxyTargetClass()) {
23             if (shouldProxyTargetClass(beanClass, beanName)) {
24                 proxyFactory.setProxyTargetClass(true);
25             }
26             else {
27                 evaluateProxyInterfaces(beanClass, proxyFactory);
28             }
29         }
30 
31         Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
32         proxyFactory.addAdvisors(advisors);
33         proxyFactory.setTargetSource(targetSource);
34         customizeProxyFactory(proxyFactory);
35 
36         proxyFactory.setFrozen(this.freezeProxy);
37         if (advisorsPreFiltered()) {
38             proxyFactory.setPreFiltered(true);
39         }
40 
41         return proxyFactory.getProxy(getProxyClassLoader());
42     }
createProxy

從上往下看
19行建立代理物件物件工廠proxyFactory ,31~34在代理物件工廠中加入增強器、目標Source等屬性
41行呼叫proxyFactory.getProxy(getProxyClassLoader()) 獲取當前bean的代理物件

 

 

先建立Aop代理物件

 

可以看到結果一系列呼叫後來到下圖,有3種動態代理物件可能返回,我們這裡返回的是Cglib動態代理物件

 

一步步將代理物件返回,執行完當前bean的 applyBeanPostProcessorsAfterInitialization方法,返回其代理物件

 

 

 

可以得知:返回的代理物件將代替bean物件存入容器中

到此為止,我們的代理物件建立步驟就完成了。


 

總結:

  initializeBean方法在初始化bean時,將通過 applyBeanPostProcessorsAfterInitialization 建立並返回目標bean的代理物件,並存入容器中。

  目前為止的過程,都是在初始化bean前完成的

  下面兩張圖中的程式碼流程是關鍵

  

  initializeBean流程:

  

  

  refresh流程(AOP中我們需要了解的):

  1. postProcessBeanFactory(beanFactory);
  2. invokeBeanFactoryPostProcessors(beanFactory);
  3. finishBeanFactoryInitialization(beanFactory);

refresh完整流程參考如下:

 1 @Override
 2     public void refresh() throws BeansException, IllegalStateException {
 3         synchronized (this.startupShutdownMonitor) {
 4             // Prepare this context for refreshing.
 5             prepareRefresh();
 6 
 7             // Tell the subclass to refresh the internal bean factory.
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
 9 
10             // Prepare the bean factory for use in this context.
11             prepareBeanFactory(beanFactory);
12 
13             try {
14                 // Allows post-processing of the bean factory in context subclasses.
15                 postProcessBeanFactory(beanFactory);
16 
17                 // Invoke factory processors registered as beans in the context.
18                 invokeBeanFactoryPostProcessors(beanFactory);
19 
20                 // Register bean processors that intercept bean creation.
21                 registerBeanPostProcessors(beanFactory);
22 
23                 // Initialize message source for this context.
24                 initMessageSource();
25 
26                 // Initialize event multicaster for this context.
27                 initApplicationEventMulticaster();
28 
29                 // Initialize other special beans in specific context subclasses.
30                 onRefresh();
31 
32                 // Check for listener beans and register them.
33                 registerListeners();
34 
35                 // Instantiate all remaining (non-lazy-init) singletons.
36                 finishBeanFactoryInitialization(beanFactory);
37 
38                 // Last step: publish corresponding event.
39                 finishRefresh();
40             }
41 
42             catch (BeansException ex) {
43                 if (logger.isWarnEnabled()) {
44                     logger.warn("Exception encountered during context initialization - " +
45                             "cancelling refresh attempt: " + ex);
46                 }
47 
48                 // Destroy already created singletons to avoid dangling resources.
49                 destroyBeans();
50 
51                 // Reset 'active' flag.
52                 cancelRefresh(ex);
53 
54                 // Propagate exception to caller.
55                 throw ex;
56             }
57 
58             finally {
59                 // Reset common introspection caches in Spring's core, since we
60                 // might not ever need metadata for singleton beans anymore...
61                 resetCommonCaches();
62             }
63         }
64     }
refresh

 

  在下一篇中,也是本系列的最後一篇,我們將探究增強器(日誌方法)是如何通過代理物件,在代理物件方法執行的時候發揮作用的。

&n