天天看點

AOP靜态代了解析2-代碼織入

當我們完成了所有的AspectJ的準備工作後便可以進行織入分析了,首先還是從LoadTimeWeaverAwareProcessor開始。

LoadTimeWeaverAwareProcessor實作BeanPostProcessor方法,那麼對于BeanPostProcessor接口來講,postProcessBeforeInitialization與postProcessAfterInitialization有着其特殊意義,也就是說在所有bean的初始化之前與之後都會分别調用對應的方法,那麼在LoadTimeWeaverAwareProcessor中的postProcessBeforeInitialization函數中完成了什麼樣的邏輯呢? 

在LoadTimeWeaverAwareProcessor中的postProcessBeforeInitialization函數中,因為最開始的if判斷注定這個後處理器隻對LoadTimeWeaverAware類型的bean起作用,而縱觀所有的bean,實作LoadTimeWeaver接口的類隻有AspectJWeavingEnabler。

當在Spring中調用AspectJWeavingEnabler時,this.loadTimeWeaver尚未被初始化,那麼,會直接調用beanFactory.getBean方法擷取對應的DefaultContextLoadTimeWeaver類型的bean,并将其設定為AspectJWeavingEnabler類型bean的loadTimeWeaver屬性中。

AspectJWeavingEnabler實作了BeanClassLoaderAware以及Ordered接口,實作BeanClassLoaderAware接口保證了在bean初始化的時候調用AbstractAutowireCapableBeanFactory的invokeAwareMethods的時候将beanClassLoader指派給目前類。而實作Ordered接口則保證在執行個體化bean時目前bean會被最先初始化。

DefaultContextLoadTimeWeaver類又同時實作了LoadTimeWeaver、BeanClassLoaderAware以及DisposableBean。其中DisposableBean接口保證在bean銷毀時會調用destroy方法進行bean的清理,而BeanClassLoaderAware接口則保證在bean的初始化調用AbstractAutowireCapableBeanFactory的invokeAwareMethods時調用setBeanClassLoader方法。

也就是經過以上程式setBeanClassLoader和postProcessBeforeInitialization的處理後,在Spring中的bean之間的關系如下:

AspectJWeavingEnabler類型的bean中的loadTimeWeaver屬性被初始化為DefaultContextLoadTimeWeaver類型的bean;

DefaultContextLoadTimeWeaver類型的bean中的loadTimeWeaver屬性被初始化為InstrumentationLoadTimeWeaver。

上面的函數中有一句很容易被忽略但是很關鍵的代碼:

this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(classLoader);

這句代碼不僅僅是執行個體化了一個InstrumentationLoadTimeWeaver類型的執行個體,而且在執行個體化過程中還做了一些額外的操作。在執行個體化過程中判斷了目前是否存在Instrumentation執行個體,最終會取InstrumentationSavingAgent類中的instrumentation的靜态屬性,判斷這個屬性是否是null,InstrumentationSavingAgent這個類是spring-instrument-3.2.9.RELEASE.jar的代理入口類,當應用程式啟動時啟動了spring-instrument-3.2.9.RELEASE.jar代理時,即在虛拟機參數中設定了-javaagent參數,虛拟機會建立Instrumentation執行個體并傳遞給premain方法,InstrumentationSavingAgent會把這個類儲存在instrumentation靜态屬性中。是以在程式啟動時啟動了代理時InstrumentationLoadTimeWeaver.isInstrumentationAvailable()這個方法是傳回true的,是以loadTimeWeaver屬性會設定成InstrumentationLoadTimeWeaver對象。對于注冊轉換器,如addTransformer函數等,便可以直接使用此屬性(instrumentation)進行操作了。

因為AspectJWeavingEnabler類同樣實作了BeanFactoryPostProcessor,是以當所有bean解析結束後會調用其postProcessBeanFactory方法。看下AspectJWeavingEnabler類的enableAspectJWeaving方法,

AspectJClassBypassingClassFileTransformer類和ClassPreProcessorAgentAdapter類都實作了位元組碼轉換接口ClassFileTransformer

 這也是一個修飾器模式,最終會調用ClassPreProcessorAgentAdapter的transform方法執行位元組碼轉換邏輯,在類加載器定義類時(即調用defineClass方法)會調用此類的transform方法來進行位元組碼轉換替換原始類。

AspectJClassBypassingClassFileTransformer的作用僅僅是告訴AspectJ以org.aspectj開頭的或者org/aspectj開頭的類不進行處理。

ClassPreProcessorAgentAdapter類中的代碼比較多,它的主要工作是解析aop.xml檔案,解析類中的Aspect注解,并且根據解析結果來生成轉換後的位元組碼。

接下來就看看InstrumentationLoadTimeWeaver類的addTransformer方法代碼:

從代碼中可以看到,這個方法中,把類轉換器actualTransformer通過instrumentation執行個體注冊給了虛拟機。這裡采用了修飾器模式,actualTransformer對transformer進行修改封裝,下面是FilteringClassFileTransformer這個内部類的代碼:

這裡面的targetClassLoader就是容器的bean類加載,在進行類位元組碼轉換之前先判斷執行類加載的加載器是否是bean類加載器,如果不是的話跳過類裝換邏輯直接傳回null,傳回null的意思就是不執行類轉換還是使用原始的類位元組碼。什麼情況下會有類加載不是bean的類加載器的情況?AbstractApplicationContext的prepareBeanFactory方法中有一行代碼:

當容器中注冊了loadTimeWeaver之後會給容器設定一個ContextTypeMatchClassLoader類型的臨時類加載器,在織入切面時隻有在bean執行個體化時織入切面才有意義,在進行一些類型比較或者校驗的時候,比如判斷一個bean是否是FactoryBean、BPP、BFPP,這時候不涉及到執行個體化,是以做位元組碼轉換沒有任何意義,而且還會增加無謂的性能消耗,是以在進行這些類型比較時使用這個臨時的類加載器執行類加載,這樣在上面的transform方法就會因為類加載不比對而跳過位元組碼轉換,這裡有一點非常關鍵的是,ContextTypeMatchClassLoader的父類加載就是容器bean類加載器,是以ContextTypeMatchClassLoader類加載器是不遵循“雙親委派”的,因為如果它遵循了“雙親委派”,那麼它的類加載工作還是會委托給bean類加載器,這樣的話if裡面的條件就不會比對,還是會執行類轉換。ContextTypeMatchClassLoader的類加載工作會委托給ContextOverridingClassLoader類對象,有興趣可以看看ContextOverridingClassLoader和OverridingClassLoader這兩個類的代碼。這個臨時的類加載器會在容器初始化快結束時,容器bean執行個體化之前被清掉,代碼在AbstractApplicationContext類的finishBeanFactoryInitialization方法: