javascript
Spring IOC BeanDefinition解析
Spring IOC BeanDefinition解析
IOC(Inversion of Control)即控制反轉,是說創建對象的控制權進行了轉移,以前創建對象的主動權和創建時機是由自己把控的,而現在這種權利轉移到Spring IOC容器。許多非凡的應用,都是由兩個或者多個類通過彼此的合作依賴來實現業務邏輯的,在Spring中,這些依賴關系可以通過把對象的依賴注入交給IOC容器來管理,這樣在解耦代碼的同時提高了代碼的可測試性。
1.??? 加載bean
加載bean的流程:
(1)???? 封裝資源文件。當進入XmlBeanDefinitionReader后首先對參數Resource使用EncodedResource類進行封裝。
(2)???? 獲取輸入流。從Resource中獲取對應的InputStream并構造InputSource。
(3)???? 通過構造的InputSource實例和Resource實例繼續調用函數doLoadBeanDefinitions。
我們來看一下doLoadBeanDefinitions函數的具體的實現過程(中間省略了loadBeanDefinitions具體方法的一步):
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {return loadBeanDefinitions(new EncodedResource(resource)); }繼續跟進代碼,進入真正的核心處理部分doLoadBeanDefinitions(inputSource, encodedResource.getResource())
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)throws BeanDefinitionStoreException {try {Document doc = doLoadDocument(inputSource, resource);return registerBeanDefinitions(doc, resource);}catch (BeanDefinitionStoreException ex) {throw ex;}// ……省略異常處理部分}?
在上面冗長的代碼中,假如不考慮異常類的代碼,其實只做了三件事,這三件事的每一件事都必不可少。
(1)???? 獲取對XML文件的驗證模式。
(2)???? 加載XML文件,并得到對應的Document。
(3)???? 根據返回的Document注冊bean信息。
2.??? 獲取XML的驗證模式
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,getValidationModeForResource(resource), isNamespaceAware());}?2.1驗證模式的讀取
了解XML文件的讀者都應該知道XML文件的驗證模式保證了XML文件的正確性,而比較常用的驗證模式有兩種:DTD和XSD。
對于驗證模式,讀者可以自行查閱數據了解。
驗證模式的讀取方法如下:
protected int getValidationModeForResource(Resource resource) {int validationModeToUse = getValidationMode();if (validationModeToUse != VALIDATION_AUTO) {return validationModeToUse;}int detectedMode = detectValidationMode(resource);if (detectedMode != VALIDATION_AUTO) {return detectedMode;}// Hmm, we didn't get a clear indication... Let's assume XSD,// since apparently no DTD declaration has been found up until// detection stopped (before finding the document's root tag).return VALIDATION_XSD;}?方法的實現其實還是很簡單的,無非是如果設定了驗證模式則使用設定的驗證模式,否則使用自動的驗證模式,自檢測驗證模式的功能相對來說比較簡單,這里就不再多說了。
3.??? 獲取Document
經過了驗證模式準備的步驟就可以進行Document加載了,同樣XmlBeanDefinitionReader對于文檔的讀取并沒有親力親為,而是委托給了DocumentLoader去執行,解析代碼如下(DefaultDocumentLoader中)
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);if (logger.isDebugEnabled()) {logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");}DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);return builder.parse(inputSource);}?對于這部分代碼其實并沒有太多可以描述的,因為通過SAX解析XML文檔的套路大致都差不多,Spring在這里并沒有什么特殊的地方,同樣首先創建DocumentBuilderFactory再通過DocumentBuilderFactory創建DocumentBuilder,進而解析inputSource來返回Document對象。
4.??? 解析及注冊BeanDefinitions
? 把文件轉換成Document之后,接下來就可以提取及注冊bean了。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {//使用DefaultBeanDefinitionDocumentReader實例化BeanDefinitionDocumentReaderBeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // 在實例化BeanDefinitionReader時候會將BeanDefinitionRegistry傳入,默認使用繼承自DefaultListableBeanFactory的子類// 記錄統計前BeanDefinition的加載個數int countBefore = getRegistry().getBeanDefinitionCount();// 加載及注冊bean documentReader.registerBeanDefinitions(doc, createReaderContext(resource));// 記錄本次加載的BeanDefinition個數return getRegistry().getBeanDefinitionCount() - countBefore;}?? 進入DefaultBeanDefinitionDocumentReader的registerBeanDefinitions方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {this.readerContext = readerContext;logger.debug("Loading bean definitions");Element root = doc.getDocumentElement();doRegisterBeanDefinitions(root);}?下面進入核心邏輯的底部doRegisterBeanDefinitions(root)方法
protected void doRegisterBeanDefinitions(Element root) {// 專門處理解析BeanDefinitionParserDelegate parent = this.delegate;this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {// 處理profile屬性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);parseBeanDefinitions(root, this.delegate);// 解析后處理,留給子類實現 postProcessXml(root);this.delegate = parent;}?
4.1profile屬性的使用
這個特性可以同時在配置文件中部署兩套配置來適用于生產環境和開發環境,這樣可以方便的進行切換開發、部署環境,最常用的是更換不同的數據庫。
4.2解析并注冊BeanDefinitions
處理了profile后就可以進行XML的讀取了,跟蹤代碼進入parseBeanDefinitions(root, this.delegate)
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {// 對beans的處理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)) {// 對bean的處理parseDefaultElement(ele, delegate);}else {// 對bean的處理delegate.parseCustomElement(ele);}}}}else {delegate.parseCustomElement(root);}}?
上面的代碼看起來邏輯還是挺清晰的,因為在spring的xml配置里面有兩大類bean申明,一個是默認的,一個是自定義的,兩種方式的讀取及解析差別還是非常大的,如果采用Spring默認的配置,spring當然知道該怎么做,但是如果是自定義的,那么久需要用戶實現一些接口及配置了。
對于標簽解析,請看我下一篇文章。
轉載于:https://www.cnblogs.com/wcj-java/p/9218239.html
總結
以上是生活随笔為你收集整理的Spring IOC BeanDefinition解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaWeb项目架构之NFS文件服务器
- 下一篇: .Net Core----关于MVC中T