關於IOC容器的初始化,結合以前SpringMVC的demo,對其過程進行一個相對詳細的梳理,主要分爲幾個部分:web
答:直接本身從源碼看第一遍,會有一個初步的認識;可是看完以後,會由於沒有實際走一遍執行而沒法驗證本身認知的過程;另外就是:由於基於接口自己會有多個實現,所以在不少狀況下,經過Ctrl+B直接會進入到接口中,可是方法的實現體爲空,經過Ctrl+Alt+B查看具體實現,會出現多個類都實現了這個方法,具體是哪個,沒法準確肯定,容易致使理解失誤spring
從SpringMVC的demo中的web.xml配置項能夠看到:express
<listener>
<listener-class>app
org.springframework.web.context.ContextLoaderListeneride
</listener-class>
</listener>post
從ContextLoaderListener進入,這是入口,以後會通過refresh()過程,refresh()過程當中會將IOC容器初始化完成,其中重點有:load階段、process階段、register階段;接下來會從入口以及後續的三個階段進行介紹。ui
進入到ContextLoaderListener類,ContextLoaderListener實現了ServletContextListener接口,該接口中有兩個default方法,分別是:this
//接收Web應用程序初始化過程正在啓動的通知spa
default public void contextInitialized(ServletContextEvent sce) {}debug
//接收ServletContext即將關閉的通知
default public void contextDestroyed(ServletContextEvent sce) {}
ContextLoaderListener類中未保持原有的default,從新作了實現:
/** * Initialize the root web application context. */ @Override public void contextInitialized(ServletContextEvent event) { initWebApplicationContext(event.getServletContext()); }
而後進入到父類ContextLoader類的initWebApplicationContext方法中,該方法中會繼續經過:configureAndRefreshWebApplicationContext
最終調用到:AbstractApplicationContext中的refresh(),至此進入到IOC的load階段(下方爲abstractApplicationContext的類圖)
進入到重要邏輯分析 ——》ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
一步步往裏走,到AbstractRefreshableApplicationContext中,具體以下:
protected final void refreshBeanFactory() throws BeansException { //判斷是否已經有beanFactory了,有則銷燬 if (hasBeanFactory()) { //銷燬Beans destroyBeans(); //將BeanFactory的全部相關內容所有置爲null,其中包含: //serializedID爲null,而且對象引用自己置爲null //即等GC了 closeBeanFactory(); } try { //建立BeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); //加載BeanDefinitions loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
從這裏的createBeanFactory()跟進去能夠進入看到是new了一個DefaultListableBeanFactory
DefaultListableBeanFactory的類圖以下:
以後下一步loadBeanDefinitions(beanFactory)——》XmlWebApplicationContext類的loadBeanDefinitions
中(這裏就是會有多個實現的狀況出現,直接經過demo單步調試跟進肯定程序走向),以後會new一個XmlBeanDefinitionReader實例,是之前面new出來的beanFactory爲參數,這個xmlReaderDefinitionReader會做爲參數傳入到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中,以後一級一級進行轉換,最終進入到:AbstractBeanDefinitionReader類中的LoadDefinitions,這個時候參數通過了前面的各類過程的轉換,最終進入到了doLoadBeanDefinitions(inputSource, encodedResource.getResource())
這裏作了層層封裝,每一次從外層進入到裏層的具體實現和調用上,主要變化的是:參數形式,能夠看到:從getConfigLocations()獲得配置文件的路徑,默認路徑是:
/** Default config location for the root context. */ public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";
再到基於獲得的配置路徑,對其進行循環loaderDefinitions,在每一層循環中,是針對單一的一個String configLocation,以後將String類型的location轉換成Resource,再轉換成InputStream,以後轉成InputSource,最終在doLoadBeanDefinitions中將直接用InputSource的實例做爲參數,層層遞進層層轉換:
//真正從特定XML文件中加載bean definitions的方法(這裏將exception的內容沒有所有放進來) protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //加載————》獲得一個doc對象。其實最後一個是DOMParser類的對象 //至此,整個過程當中不斷去將File先解析以後再轉換成輸入流,再最終獲得Document doc,加載完成 Document doc = doLoadDocument(inputSource, resource); //註冊:具體作的事項是: //一、解析xml //二、將解析獲得的BeanName和beanDefinition都存儲到beanDefinitionMap中進行註冊 int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; }
其中,會先doLoadDocument方法調用獲得一個DomParser的對象;至此加載完成;而後進入到registerBeanDefinitions的過程,其中:會有process過程 + register過程
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //new一個BeanDefinitionDocumentReader對象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //註冊BeanDefinitions //使用documentReader實例調用registerBeanDefinitions方法,具體操做內容包含: //(1)BeanDefinitionDocumentReader處理Document元素時,將Document元素的解析工做委託給 // BeanDefinitionParserDelegate處理,其中會將beanName作一個惟一值的轉換,爲後續的register到map中提供前提 //(2)判斷BeanDefinitionMap中是否有某個k,v已經存在,有的話更新Definition的v值,無則直接put k,v //DefaultBeanDefinitionDocumentReader 實現了 BeanDefinitionDocumentReader接口 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }
其中process過程在registetBeanDefinitions跟進到下一步中,即:DefaultBeanDefinitionDocumentReader類的registerBeanDefinitions方法,看到:doRegisterBeanDefinitions方法,看到這裏就能夠興奮一下啦,真正作事情的來了:
其中關鍵步驟就兩部分:第一部分是createDelegate並賦值給delegate字段;以後進行processxml三步曲 preprocess、parse、postprocess
protected void doRegisterBeanDefinitions(Element root) { //先賦值給parent,以後建立createDelegate從新賦值給delegate BeanDefinitionParserDelegate parent = this.delegate; //在DefaultBeanDefinitionDocumentReader處理Document元素時, // 將Document文檔內元素具體解析工做委託給BeanDefinitionParserDelegate類來處理 this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //預處理Xml preProcessXml(root); //解析BeanDefinitions parseBeanDefinitions(root, this.delegate); //後置處理Xml postProcessXml(root); this.delegate = parent; }
進入到parseBeanDefinitions(root, this.delegate)中,跟到具體實現中:
//處理給定的bean節點,解析bean definitions,而且經過registry進行註冊 protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //這個BeanDefinitionHolder從命名上來看: //分析含義是:用來包裹存儲一個BeanDefinition的容器,由於叫作Holder //實際是:確實是用來存儲這個BeanDefinition的包裹,其中對beanName作了一個特定處理,將之轉換成了惟一值 //而且進行詳細的解析,包含:對於set、list、map等 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // Register the final decorated instance. //獲得了一個自認爲完備的beanDefinition了,而後進行註冊 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); } }
從上面代碼中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())會進入到類BeanDefinitionUtils對static方法registerBeanDefinition的調用:
public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //根據name進行註冊,由於name是惟一性的了 //具體裏面就是直接調用了map.put,將其放入到beanDefinitionMap中 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
以後跟進到registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 最終會進入到DefaultListableBeanFactory類中:
摘取方法中的關鍵邏輯:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { //從map中取出beanName的key對應的Definition的對象 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { } //爲map中的beanName的key值從新設置對應的v值 this.beanDefinitionMap.put(beanName, beanDefinition); }
其中beanDefintionMap的定義以下:
/** Map of bean definition objects, keyed by bean name. */ private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
2、將以上梳理的整個過程經過時序圖的方式,更清晰地展示出調用關係:(圖片若沒法看清的,能夠查看大圖或者下載)