逛技术论坛的时候,不知道是一位刚学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容器中的启动)