逛技術論壇的時候,不知道是一位剛學Java的新手,或者是一個工作了好幾年沒有使用過spring框架的開發者在論壇提出了這樣一個問題:spring中的IOC有什么好的?想來這個問題這個大家心里都會立即說出IOC是spring的核心思想,叫控制反轉也叫依賴注入。我在后續的回復里看到了太多的IOC其實就是控制反轉和依賴注入。其實我剛剛也在回復欄里輕率的回復着IOC也就是讓你在做一些應用的時候在沒有spring之前的時候只能代碼做改變的時候,現在通過配置文件就能夠改變他們之間的關系,依賴注入本人的理解到覺得xml文件中ref這個標簽是最好能夠結實依賴這層思想。但是細細一想,這些回答怎么看都是千遍一律,雖說不能說是錯誤的,但總給人一種似曾相識,但要深入去體會的時候,又會覺得似乎無從下手的感覺。心里默默的覺得一驚,雖說現在用的spring也挺久了,但是讓自己跳出這些看似千遍一律的答案又似乎說不出個所以然。只好默默的去google了。
現在分享下自己Google完對於spring中的IOC 理解吧。spring中的IOC其實是一個容器,至於什么控制反轉依賴注入只不過是IOC這個容器這個spring核心在創造的過程中的指導思想,也就是說我在編寫spring這個應用的時候我的目標就是為了在開發一個企業應用的時候更多的簡化很多復雜繁瑣的重復性工作,將以前只能在代碼層做更改的東西,全部放在配置文件層面來解決。以前對象與對象之間在代碼層產生的耦合關系,通過配置文件來產生耦合關系,而通過配置文件來產生耦合關系的對象與對象又可以輕易的解耦。這個就是spring的優勢。不知道大家考慮過這個問題,我們很多對象都通過配置文件來對象化,我們在程序中需要這些對象進行一些行為動作,總要有個地方存放他們吧。忽然想起大神的一句話:“任何配置元素的對象化,任何框架都需要一個容身之所”,IOC便就是這些配置元素對象化的容身之所。似乎將容身之所還是會讓很多人難以理解,我知道大家肯定搭建過一個spring環境的應用,肯定也與spring的配置文件打過交道,我當初接觸這些配置文件的時候能夠依樣畫葫蘆照着別人的配置文件能夠糊里糊塗的搭建成功,但是對於spring是如何把配置文件讀取進去,腦袋里是沒有一絲的輪廓的。下面是spring中XmlBeanDefinitionReader中就是如何將配置文件讀取到程序中的具體過程。
1 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 具體的注冊過程,首先得到XmlBeanDefinitionReader,來處理xml的bean定義文件2 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); 3 documentReader.setEnvironment(getEnvironment()); 4 int countBefore = getRegistry().getBeanDefinitionCount(); 5 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); 6 return getRegistry().getBeanDefinitionCount() - countBefore; 7 }
具體的在BeanDefinitionDocumentReader中完成對,下面是一個簡要的注冊過程來完成bean定義文件的解析和IOC容器中bean的初始化
1 public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { 2 this.readerContext = readerContext; 3 4 logger.debug("Loading bean definitions"); 5 Element root = doc.getDocumentElement(); 6 7 BeanDefinitionParserDelegate delegate = createHelper(readerContext, root); 8 9 preProcessXml(root); 10 parseBeanDefinitions(root, delegate); 11 postProcessXml(root); 12 } 13 14 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 15 if (delegate.isDefaultNamespace(root.getNamespaceURI())) { 16 //這里得到xml文件的子節點,比如各個bean節點 17 NodeList nl = root.getChildNodes(); 18 19 //這里對每個節點進行分析處理 20 for (int i = 0; i < nl.getLength(); i++) { 21 Node node = nl.item(i); 22 if (node instanceof Element) { 23 Element ele = (Element) node; 24 String namespaceUri = ele.getNamespaceURI(); 25 if (delegate.isDefaultNamespace(namespaceUri)) { 26 //這里是解析過程的調用,對缺省的元素進行分析比如bean元素 27 parseDefaultElement(ele, delegate); 28 } 29 else { 30 delegate.parseCustomElement(ele); 31 } 32 } 33 } 34 } else { 35 delegate.parseCustomElement(root); 36 } 37 } 38 39 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { 40 //這里對元素Import進行處理 41 if (DomUtils.nodeNameEquals(ele, IMPORT_ELEMENT)) { 42 importBeanDefinitionResource(ele); 43 } 44 else if (DomUtils.nodeNameEquals(ele, ALIAS_ELEMENT)) { 45 String name = ele.getAttribute(NAME_ATTRIBUTE); 46 String alias = ele.getAttribute(ALIAS_ATTRIBUTE); 47 getReaderContext().getReader().getBeanFactory().registerAlias(name, alias); 48 getReaderContext().fireAliasRegistered(name, alias, extractSource(ele)); 49 } 50 //這里對我們最熟悉的bean元素進行處理 51 else if (DomUtils.nodeNameEquals(ele, BEAN_ELEMENT)) { 52 //委托給BeanDefinitionParserDelegate來完成對bean元素的處理,這個類包含了具體的bean解析的過程。 53 // 把解析bean文件得到的信息放到BeanDefinition里,他是bean信息的主要載體,也是IOC容器的管理對象。 54 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); 55 if (bdHolder != null) { 56 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); 57 // 這里是向IOC容器注冊,實際上是放到IOC容器的一個map里 58 BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); 59 60 // 這里向IOC容器發送事件,表示解析和注冊完成。 61 getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); 62 } 63 } 64 }
看完了整段代碼算是有個初步的理解,也就是說,應用項目在web容器的時候加載這些配置文件,然后更具這些配置文件前的標示例如(ID,REF)之類的標簽來區分各個的作用然后通過Java反射生成想對應的實例對象,通過一個Map將配置文件讀取到的文本作為key值,反射生成的實例作為value值存進Map中進行維護。這樣,所有的配置文件對象化就有個棲身之所了,這個也是IOC容器的作用。寫到這里,突然覺得工廠模式讓IOC這種配置對象化體現的淋漓盡致,你無須關注對象是如何生成的,你只需關注你需要什么樣的配置,在你的配置文件中描述清楚,然后教個xmlFactoryBean這個類去生產就能拿到你所有的對象。
在我眼中的IOC就是spring的核心,是一個配置元素對象化的容身之所。至於IOC中的依賴注入我覺得用配置文件中的標簽“ref”這個標簽就能很好的體現了。
參考資料:
http://www.iteye.com/topic/86339(IOC容器)
http://www.iteye.com/topic/86594(IOC容器在web容器中的啟動)