相關背景及資源:html
曹工說Spring Boot源碼(1)-- Bean Definition究竟是什麼,附spring思惟導圖分享java
曹工說Spring Boot源碼(2)-- Bean Definition究竟是什麼,我們對着接口,逐個方法講解git
曹工說Spring Boot源碼(3)-- 手動註冊Bean Definition不比遊戲好玩嗎,咱們來試一下spring
曹工說Spring Boot源碼(4)-- 我是怎麼自定義ApplicationContext,從json文件讀取bean definition的?express
曹工說Spring Boot源碼(5)-- 怎麼從properties文件讀取beanjson
曹工說Spring Boot源碼(6)-- Spring怎麼從xml文件裏解析bean的緩存
曹工說Spring Boot源碼(7)-- Spring解析xml文件,到底從中獲得了什麼(上)app
曹工說Spring Boot源碼(8)-- Spring解析xml文件,到底從中獲得了什麼(util命名空間)spring-boot
曹工說Spring Boot源碼(9)-- Spring解析xml文件,到底從中獲得了什麼(context命名空間上)工具
曹工說Spring Boot源碼(10)-- Spring解析xml文件,到底從中獲得了什麼(context:annotation-config 解析)
曹工說Spring Boot源碼(11)-- context:component-scan,你真的會用嗎(此次來講說它的奇技淫巧)
曹工說Spring Boot源碼(12)-- Spring解析xml文件,到底從中獲得了什麼(context:component-scan完整解析)
曹工說Spring Boot源碼(13)-- AspectJ的運行時織入(Load-Time-Weaving),基本內容是講清楚了(附源碼)
曹工說Spring Boot源碼(14)-- AspectJ的Load-Time-Weaving的兩種實現方式細細講解,以及怎麼和Spring Instrumentation集成
曹工說Spring Boot源碼(15)-- Spring從xml文件裏到底獲得了什麼(context:load-time-weaver 完整解析)
曹工說Spring Boot源碼(16)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【上】)
曹工說Spring Boot源碼(17)-- Spring從xml文件裏到底獲得了什麼(aop:config完整解析【中】)
曹工說Spring Boot源碼(18)-- Spring AOP源碼分析三部曲,終於快講完了 (aop:config完整解析【下】)
曹工說Spring Boot源碼(19)-- Spring 帶給咱們的工具利器,建立代理不用愁(ProxyFactory)
曹工說Spring Boot源碼(20)-- 碼網恢恢,疏而不漏,如何記錄Spring RedisTemplate每次操做日誌
曹工說Spring Boot源碼(21)-- 爲了讓你們理解Spring Aop利器ProxyFactory,我已經拼了
曹工說Spring Boot源碼(22)-- 你說我Spring Aop依賴AspectJ,我依賴它什麼了
曹工說Spring Boot源碼(23)-- ASM又立功了,Spring原來是這麼遞歸獲取註解的元註解的
曹工說Spring Boot源碼(24)-- Spring註解掃描的瑞士軍刀,asm技術實戰(上)
曹工說Spring Boot源碼(25)-- Spring註解掃描的瑞士軍刀,ASM + Java Instrumentation,順便提提Jar包破解
曹工說Spring Boot源碼(26)-- 學習字節碼也太難了,實在不能忍受了,寫了個小小的字節碼執行引擎
曹工說Spring Boot源碼(27)-- Spring的component-scan,光是include-filter屬性的各類配置方式,就夠玩半天了
曹工說Spring Boot源碼(28)-- Spring的component-scan機制,讓你本身來進行簡單實現,怎麼辦
工程結構圖:
在獲取單例bean的時候,會進入如下方法:
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean) protected Object getSingleton(String beanName, boolean allowEarlyReference) { // 1 Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // 2 singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { // 3 ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 4 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
這裏面涉及到了該類中的三個field。
/** 1級緩存 Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 2級緩存 Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16); /** 3級緩存 Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
接着說前面的代碼。
1處,在最上層的緩存singletonObjects
中,獲取單例bean,這裏面拿到的bean,直接可使用;若是沒取到,則進入2處
2處,在2級緩存earlySingletonObjects
中,查找bean;
3處,若是在2級緩存中,仍是沒找到,則在3級緩存中查找對應的工廠對象,利用拿到的工廠對象(工廠對象中,有3個field,一個是beanName,一個是RootBeanDefinition,一個是已經建立好的,但尚未注入屬性的bean),去獲取包裝後的bean,或者說,代理後的bean。
什麼是已經建立好的,但沒有注入屬性的bean?
好比一個bean,有10個字段,你new了以後,對象已經有了,內存空間已經開闢了,堆裏已經分配了該對象的空間了,只是此時的10個field仍是null。
說實話,若是簡單寫寫的話,一級緩存都沒問題。給你們看一個我之前寫的渣渣ioc容器:
曹工說Tomcat4:利用 Digester 手擼一個輕量的 Spring IOC容器
@Data public class BeanDefinitionRegistry { /** * map:存儲 bean的class-》bean實例 */ private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>(); /** * 根據bean 定義獲取bean * 一、先查bean容器,查到則返回 * 二、生成bean,放進容器(此時,依賴還沒注入,主要是解決循環依賴問題) * 三、注入依賴 * * @param beanDefiniton * @return */ private Object getBean(MyBeanDefiniton beanDefiniton) { Class<?> beanClazz = beanDefiniton.getBeanClazz(); Object bean = beanMapByClass.get(beanClazz); if (bean != null) { return bean; } // 0 bean = generateBeanInstance(beanClazz); // 1 先行暴露,解決循環依賴問題 beanMapByClass.put(beanClazz, bean); beanMapByName.put(beanDefiniton.getBeanName(), bean); // 2 查找依賴 List<Field> dependencysByField = beanDefiniton.getDependencysByField(); if (dependencysByField == null) { return bean; } // 3 for (Field field : dependencysByField) { try { autowireField(beanClazz, bean, field); } catch (Exception e) { throw new RuntimeException(beanClazz.getName() + " 建立失敗",e); } } return bean; } }
你們看上面的代碼,我只定義了一個field,就是一個map,存放bean的class-》bean。
/** * map:存儲 bean的class-》bean實例 */ private Map<Class, Object> beanMapByClass = new ConcurrentHashMap<>();
上面這個代碼,有啥問題沒?spring爲啥整整三級?
一級緩存的問題在於,就1個map,裏面既有完整的已經ready的bean,也有不完整的,還沒有設置field的bean。
若是這時候,有其餘線程去這個map裏獲取bean來用怎麼辦?拿到的bean,不完整,怎麼辦呢?屬性都是null,直接空指針了。
因此,咱們就要加一個map,這個map,用來存放那種不完整的bean。這裏,仍是拿spring舉例。咱們能夠只用下面這兩層:
/** 1級緩存 Cache of singleton objects: bean name to bean instance. */ private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); /** 2級緩存 Cache of early singleton objects: bean name to bean instance. */ private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
由於spring代碼裏是三級緩存,因此咱們對源碼作一點修改。
建立了bean以後,屬性注入以前,將建立出來的不完整bean,放到earlySingletonObjects
這個代碼,在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean
,我這邊只有4.0版本的spring源碼工程,不過這套邏輯,算是spring核心邏輯,和5.x版本差異不大。
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 1 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); ... boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 2 earlySingletonObjects.put(beanName,bean); registeredSingletonObjects.add(beanName); // 3 // addSingletonFactory(beanName, new ObjectFactory() { // public Object getObject() throws BeansException { // return getEarlyBeanReference(beanName, mbd, bean); // } // }); }
org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String, boolean)
以前的代碼是文章開頭那樣的,我這裏修改成:
protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); return singletonObject; } } return (singletonObject != NULL_OBJECT ? singletonObject : null);
這樣,就是隻用兩級緩存了。
ioc循環依賴,一點問題都沒有,徹底夠用了。
我這邊一個簡單的例子,
public class Chick{ private Egg egg; public Egg getEgg() { return egg; } public void setEgg(Egg egg) { this.egg = egg; } }
public class Egg { private Chick chick; public Chick getChick() { return chick; } public void setChick(Chick chick) { this.chick = chick; }
<bean id="chick" class="foo.Chick" lazy-init="true"> <property name="egg" ref="egg"/> </bean> <bean id="egg" class="foo.Egg" lazy-init="true"> <property name="chick" ref="chick"/> </bean>
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext( "context-namespace-test-aop.xml"); Egg egg = (Egg) ctx.getBean(Egg.class);
結論:
因此,一級緩存都能解決的問題,二級固然更沒問題。
可是,若是我這裏給上面的Egg類,加個切面(aop的邏輯,意思就是最終會生成Egg的一個動態代理對象),那還有問題沒?
<aop:config> <aop:pointcut id="mypointcut" expression="execution(public * foo.Egg.*(..))"/> <aop:aspect id="myAspect" ref="performenceAspect"> <aop:after method="afterIncubate" pointcut-ref="mypointcut"/> </aop:aspect> </aop:config>
注意這裏的切點:
execution(public * foo.Egg.*(..))
就是切Egg類的方法。
加了這個邏輯後,咱們繼續運行,在 Egg egg = (Egg) ctx.getBean(Egg.class);
行,會拋出以下異常:
我塗掉了一部分,由於那是官方對這個異常的推論,由於咱們改了代碼,因此推論不許確,所以乾脆隱去。
這個異常是說:
兄弟啊,bean egg已經被注入到了其餘bean:chick中。(由於咱們循環依賴了),可是,注入到chick中的,是Egg類型。可是,咱們這裏最後對egg這個bean,進行了後置處理,生成了代理對象。那其餘bean裏,用原始的bean,是否是不太對啊?
因此,spring給咱們拋錯了。
怎麼理解呢? 以io流舉例,咱們一開始都是用的原始字節流,而後給別人用的也是字節流,可是,最後,我感受不方便,我本身悄悄弄了個緩存字符流(類比代理對象),我是方便了,可是,別人用的,仍是原始的字節流啊。
你bean不是單例嗎?不能這麼玩吧?
因此,這就是二級緩存,不能解決的問題。
什麼問題?aop情形下,注入到其餘bean的,不是最終的代理對象。
要解決這個問題,必須在其餘bean(chick),來查找咱們(以上面例子爲例,咱們是egg)的時候,查找到最終形態的egg,即代理後的egg。
怎麼作到這點呢?
加個三級緩存,裏面不存具體的bean,裏面存一個工廠對象。經過工廠對象,是能夠拿到最終形態的代理後的egg。
ok,咱們將前面修改的代碼還原:
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) { BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { // 1 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null); Class beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // 2 // Map<String, Object> earlySingletonObjects = this.getEarlySingletonObjects(); // earlySingletonObjects.put(beanName,bean); // // Set<String> registeredSingletonObjects = this.getRegisteredSingletonObjects(); // registeredSingletonObjects.add(beanName); // 3 addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { return getEarlyBeanReference(beanName, mbd, bean); } }); }
1處,建立bean,單純new,不注入
2處,revert咱們的代碼
3處,這裏new了一個ObjectFactory,而後會存入到以下的第三級緩存。
/** 3級緩存 Cache of singleton factories: bean name to ObjectFactory. */ private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
注意,new一個匿名內部類(假設這個匿名類叫AA)的對象,其中用到的外部類的變量,都會在AA中隱式生成對應的field。
你們看上圖,裏面的3個字段,和下面代碼1處中的,幾個字段,是一一對應的。
addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { // 1 return getEarlyBeanReference(beanName, mbd, bean); } });
ok,如今,egg已經把本身存進去了,存在了第三級緩存,1級和2級都沒有,那後續chick在使用getSingleton查找egg的時候,就會進入下面的邏輯了(就是文章開頭的那段代碼,下面已經把咱們的修改還原了):
protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Object singletonObject = this.singletonObjects.get(beanName); // if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { // synchronized (this.singletonObjects) { // singletonObject = this.earlySingletonObjects.get(beanName); // return singletonObject; // } // } // return (singletonObject != NULL_OBJECT ? singletonObject : null); Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { ObjectFactory singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { // 1 singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return (singletonObject != NULL_OBJECT ? singletonObject : null); }
上面就會進入1處,調用singletonFactory.getObject();
。
而前面咱們知道,這個factory的邏輯是:
addSingletonFactory(beanName, new ObjectFactory() { public Object getObject() throws BeansException { // 1 return getEarlyBeanReference(beanName, mbd, bean); } });
1處就是這個工廠方法的邏輯,這裏面,簡單說,就會去調用各個beanPostProcessor的getEarlyBeanReference方法。
其中,主要就是aop的主力beanPostProcessor,AbstractAutoProxyCreator#getEarlyBeanReference
其實現以下:
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException { Object cacheKey = getCacheKey(bean.getClass(), beanName); this.earlyProxyReferences.add(cacheKey); // 1 return wrapIfNecessary(bean, beanName, cacheKey); }
這裏的1處,就會去對egg這個bean,建立代理,此時,返回的對象,就是個代理對象了,那,注入到chick的,天然也是代理後的egg了。
咱們上面說的那個getEarlyBeanReference
就在這個接口中。
這個接口繼承了BeanPostProcessor
。
而建立代理對象,目前就是在以下兩個方法中去建立:
public interface BeanPostProcessor { Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }
這兩個方法,都是在實例化以後,建立代理。那咱們前面建立代理,是在依賴解析過程當中:
public interface SmartInstantiationAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessor { ... Object getEarlyBeanReference(Object bean, String beanName) throws BeansException; }
因此,spring但願咱們,在這幾處,要返回一樣的對象,即:既然你這幾處都要返回代理對象,那就不能返回不同的代理對象。
文章用到的aop循環依賴的demo,本身寫一個也能夠,很簡單:
http://www.javashuo.com/article/p-smkruniq-nt.html
若是有問題,歡迎指出;歡迎加羣討論;有幫助的話,請點個贊吧,謝謝