Spring系列(五) 容器初始化過程原始碼
IoC/DI 的概念
容器是Spring的核心之一(另一個核心是AOP). 有了容器, IOC/">IOC才可能實現.
-
什麼使IoC? IoC就是將類自身管理的與其由依賴關係的物件的建立/關聯和管理交予容器實現, 容器按照配置(比如xml檔案)來組織應用物件的建立和關聯.
-
什麼使DI? DI是IoC的實現方式, 由容器在程式初始化的時候將類的依賴物件注入進去.
-
IoC和DI的關係? IoC(Inversion of Control)是一種設計原則, 可以減少程式碼的耦合度, DI(Dependency Injection)是IOC的具體實現方式, 還有其他的實現方式如 DL(Dependency Lookup).
Spring 容器
ClassPathXmlApplicationContext
類應該都比較熟悉, 從熟悉的事物開始尋找線索.
下載Spring原始碼後用idea開啟, 找到類 ClassPathXmlApplicationContext
, idea可以使用 ctrl+N
輸入類名搜尋, 開啟原始檔, 按 ctrl+Alt+U
可以生成類圖.
BeanFactory
和 ResourceLoader
是兩個頂層介面. BeanFactory
是Bean的工廠,定義了IoC基本的功能. ResourceLoader
是資源載入的策略介面,定義了載入資源的基本規範, ApplicationContext
需要此介面的功能.
BeanFactory
提供了容器最基本的功能, 其中定義的方法會頻繁使用, 介面定義如下:
public interface BeanFactory { // 一個標記, 帶有此標記開頭的類不是bean, 而是工廠本身 String FACTORY_BEAN_PREFIX = "&"; // 下面幾個方法是各種獲取bean的方式 Object getBean(String name) throws BeansException; <T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; <T> T getBean(Class<T> requiredType) throws BeansException; <T> T getBean(Class<T> requiredType, Object... args) throws BeansException; // 判斷bean是否存在 boolean containsBean(String name); // bean作用域是否單例 boolean isSingleton(String name) throws NoSuchBeanDefinitionException; // bean作用域是否原型 boolean isPrototype(String name) throws NoSuchBeanDefinitionException; // bean是否與給定解析型別匹配 boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException; boolean isTypeMatch(String name, @Nullable Class<?> typeToMatch) throws NoSuchBeanDefinitionException; // 獲取bean型別 @Nullable Class<?> getType(String name) throws NoSuchBeanDefinitionException; //獲取bean別名陣列 String[] getAliases(String name); }
ApplicationContext
擴充套件了 BeanFactory
的功能, 除了作為工廠外, 它還提供了訊息國際化( MessageSource
), 獲取環境bean( EnvironmentCapable
), 容器訊息釋出( ApplicationEventPublisher
)等功能. 因為它包含了容器以外的這些功能, 所以對了解容器來說多少會產生干擾. 事實上, 檢視 BeanFactory
的子類(在類圖上選中類,或者在原始碼檢視中按 Ctrl+Alt+B
)能從它的實現中找到 DefaultListableBeanFactory
, 從名稱上二者在繼承該關係上應該比較近, 功能也比較純粹, 沒有類似ApplicationContext的其他干擾.
DefaultListableBeanFactory
類是最基本的容器實現類, 它的繼承關係如下圖. 作為bean的工廠, 它的職責就是生產bean, 基本功能正是頂級介面 BeanFactory
定義的那些方法. 它上級的介面擴充套件了自動裝配的能力( AutowireCapableBeanFactory
), 註冊和獲取等操作 BeanDefinition
例項的能力( BeanDefinitionRegistry
).
BeanDefinition
BeanDefinition 用來抽象bean定義在spring中的抽象, 最終spring將外部配置的bean轉化為 BeanDefinition
的例項儲存.
容器初始化過程
容器初始化過程分為三步, 資源Resource定位, 解析載入, 註冊.
DefaultListableBeanFactory
是工廠, 繼承它的子類只有一個 XmlBeanFactory
, 它被標註為 @Deprecated
.所以不應該在應用中使用該類, 但它可以作為了解原始碼的入口. 它有個 XmlBeanDefinitionReader
的私有變數直接new初始化, 引數this將工廠例項傳給這個物件, 這樣它就有了工廠的引用, 方便內部處理.
public class XmlBeanFactory extends DefaultListableBeanFactory { private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); public XmlBeanFactory(Resource resource) throws BeansException { this(resource, null); } public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { super(parentBeanFactory); // 這個是呼叫實際載入資源的方法 this.reader.loadBeanDefinitions(resource); }
大概串一下初始化的執行流程:
Resource DefaultListableBeanFactory XmlBeanDefinitionReader BeanDefinition
載入XML Bean的關鍵程式碼
下面按照呼叫關係跟蹤程式碼, 忽略其他的xml元素, 最終目標是找到載入註冊bean的機制.
XmlBeanDefinitionReader
--> loadBeanDefinitions
--> doLoadBeanDefinitions
--> registerBeanDefinitions
DefaultBeanDefinitionDocumentReader: BeanDefinitionDocumentReader
--> registerBeanDefinitions
--> doRegisterBeanDefinitions
--> parseBeanDefinitions
--> parseDefaultElement / processBeanDefinition
BeanDefinitionParserDelegate
--> parseBeanDefinitionElement
BeanDefinitionReaderUtils
--> registerBeanDefinition
DetaultListableBeanFactory
--> registerBeanDefinition
一. XmlBeanDefinitionReader.loadBeanDefinitions(EncodedResource encodedResource)
生成InputSource物件(用來初始化XML Dom物件)
InputStream inputStream = encodedResource.getResource().getInputStream(); try { // 生成例項, 後面用來載入dom InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } // 生成InputSource後,呼叫這個方法 return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); }
二. XmlBeanDefinitionReader.doLoadBeanDefinitions(InputSource inputSource, Resource resource)
載入生成Xml Document物件
// 生成doc例項 Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource);
三. XmlBeanDefinitionReader.registerBeanDefinitions(Document doc, Resource resource)
// 生成BeanDefinitionDocumentReader的例項, 預設實現為生成DefaultBeanDefinitionDocumentReader類的例項, 通過BeanUtil工具的例項化方法生成 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); // 傳入doc和資源的上下文物件, 註冊bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
四. registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
BeanDefinitionDocumentReader
是介面, 實現類為 DefaultBeanDefinitionDocumentReader
@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; logger.debug("Loading bean definitions"); // 獲取根節點 Element root = doc.getDocumentElement(); // 從根節點開始, 呼叫的這個方法會遞迴子節點 doRegisterBeanDefinitions(root); }
五. doRegisterBeanDefinitions(Element root)
類為 DefaultBeanDefinitionDocumentReader
protected void doRegisterBeanDefinitions(Element root) { //任何巢狀的<beans>元素都將導致此方法的遞迴。為了正確傳播和保留<beans> default- *屬性,請跟蹤當前(父)委託,該委託可以為null。建立新的(子)委託,引用父項以進行回退,然後最終將this.delegate重置為其原始(父)引用。此行為模擬了一堆代理,而實際上並不需要一個代理。 BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { // 下面這一塊程式碼主要是做profile檢查, 沒有啟用profile的bean不載入, 將直接return String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isInfoEnabled()) { logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } preProcessXml(root); // 具體解析的方法, pre和post的在這個類中為空方法 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent; }
六. parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)
類為 DefaultBeanDefinitionDocumentReader
/** * 解析文件中的根節點 * "import", "alias", "bean". */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // 是根節點,就獲取子節點, 遍歷,如果是根"import", "alias", "bean", 就呼叫parseDefaultElement, 否則parseCustomElement if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
七. parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
類為 DefaultBeanDefinitionDocumentReader
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { // 解析"import"元素, 這個方法會定位import的資源位置並重復第一步開始的步驟 importBeanDefinitionResource(ele); } else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { // 別名"alias"註冊 processAliasRegistration(ele); } else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { // 前方高能... 處理bean元素 processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // 遞迴"beans" doRegisterBeanDefinitions(ele); } }
八. processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)
類為 DefaultBeanDefinitionDocumentReader
/** *處理bean元素的定義, 並且註冊 */ protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { // 獲取bean的包裝物件,程式碼見第九步 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { // 註冊最終的bean裝飾物件 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)); } }
九. parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean)
類為 BeanDefinitionParserDelegate
關注兩個例項化過程,一個是 BeanDefinition
, 一個是其裝飾物件 BeanDefinitionHolder
的例項
/** * 解析bean元素, 可能會返回null, 如果有錯誤則報告給 * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */ @Nullable public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isDebugEnabled()) { logger.debug("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } if (containingBean == null) { checkNameUniqueness(beanName, aliases, ele); } // 例項化一個 BeanDefination 例項 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isDebugEnabled()) { logger.debug("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null; }
十. registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
是類的靜態方法 BeanDefinitionReaderUtils
;給registry物件呼叫
// 註冊bean的最終方法 public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // 使用首要名稱註冊bean String beanName = definitionHolder.getBeanName(); // 註冊bean, 具體實現在類DetaultListableBeanFactory中 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); // 註冊bean的別名 String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } } }
十一. registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
在類 DetaultListableBeanFactory
@Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { .... BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { .... // 存到map裡面 this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } }
到目前為止, bean就註冊到工廠裡面去了, 實際上工廠裡面儲存了BeanDefinition的一個對映Map, 這樣有助於Spring做一些驗證, 當獲取bean的時候也可以方便實現懶載入.