Spring 源码阅读五 IOC容器初始化之bean定义载入
2016-10-21 14:45
861 查看
上章说到要带各位去看看bean定义载入的要义,上章追寻到AbstractRefreshableApplicationContext类中的refreshBeanFactory方法
其实就是loadBeanDefinitions这个方法的具体实现步骤,下面我们跟随这个方法去看下它到底是如何载入bean定义的。由于AbstractRefreshableApplicationContext中的loadBeanDefinitions是个抽象方法,也算是个模板方法。FileSystemXmlApplicationContext中的loadBeanDefinitions方法是由AbstractXmlApplicationContext抽象类实现的。
这个方法中不难发现,try块中的代码才是载入bean定义的真正过程,我们一步一步的扒开bean定义的载入,spring将资源返回的输入流包装以后传给了doLoadBeanDefinitions方法,我们进去看看发生了什么。
可以看到,spring采用documentLoader将资源转换成了Document接口,这正是我们熟知的SAX对XML解析的重要接口之一,这下不难理解了,可以想象出spring一定是根据XSD文件规定的XML格式,解析了XML文件中的各个节点以及属性。尽管如此,我们还是跟着registerBeanDefinitions方法进去看看。此处该方法不再贴出代码,请各位自己跟踪进去看,这个方法里记录了一共注册了多少个bean定义。最终能看出端倪的地方在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中,如下代码。
这里分了两种解析路线,一个是默认的,一个是自定义的,从这里我们可以看出,我们是可以在spring的配置文件中自定义节点的。
再往下走就基本上到了spring开始针对具体标签解析的过程,各位如果有兴趣可以自行跟进去看一下spring是如何对XML文件的各个节点和属性进行解析的,了解这个过程可以帮助你熟练的掌握spring中的XML配置文件的各个节点和属性的含义。
这里我要稍稍总结一下,spring对bean定义的载入有很多种方式,读取的过程是可插拔的,不论何种形式,spring的IOC容器只要获得了bean定义信息,都可以正常工作。而我们熟知的配置读取方式就是XML文件,如果你希望,可以自己定制配置信息的读取过程,有时间我会研究下spring留给我们扩展的接口在哪里。只要找到了这个入口,那么读取配置信息就任由我们宰割了。
其实就是loadBeanDefinitions这个方法的具体实现步骤,下面我们跟随这个方法去看下它到底是如何载入bean定义的。由于AbstractRefreshableApplicationContext中的loadBeanDefinitions是个抽象方法,也算是个模板方法。FileSystemXmlApplicationContext中的loadBeanDefinitions方法是由AbstractXmlApplicationContext抽象类实现的。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); loadBeanDefinitions(beanDefinitionReader); }
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); } }
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isInfoEnabled()) { logger.info("Loading XML bean definitions from " + encodedResource.getResource()); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { InputStream inputStream = encodedResource.getResource().getInputStream(); try { InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } } }
这个方法中不难发现,try块中的代码才是载入bean定义的真正过程,我们一步一步的扒开bean定义的载入,spring将资源返回的输入流包装以后传给了doLoadBeanDefinitions方法,我们进去看看发生了什么。
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { Document doc = doLoadDocument(inputSource, resource); return registerBeanDefinitions(doc, resource); } catch (BeanDefinitionStoreException ex) { throw ex; } catch (SAXParseException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex); } catch (SAXException ex) { throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", ex); } catch (ParserConfigurationException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, ex); } catch (IOException ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, ex); } catch (Throwable ex) { throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, ex); } }
可以看到,spring采用documentLoader将资源转换成了Document接口,这正是我们熟知的SAX对XML解析的重要接口之一,这下不难理解了,可以想象出spring一定是根据XSD文件规定的XML格式,解析了XML文件中的各个节点以及属性。尽管如此,我们还是跟着registerBeanDefinitions方法进去看看。此处该方法不再贴出代码,请各位自己跟踪进去看,这个方法里记录了一共注册了多少个bean定义。最终能看出端倪的地方在DefaultBeanDefinitionDocumentReader的parseBeanDefinitions方法中,如下代码。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { 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); } }
这里分了两种解析路线,一个是默认的,一个是自定义的,从这里我们可以看出,我们是可以在spring的配置文件中自定义节点的。
再往下走就基本上到了spring开始针对具体标签解析的过程,各位如果有兴趣可以自行跟进去看一下spring是如何对XML文件的各个节点和属性进行解析的,了解这个过程可以帮助你熟练的掌握spring中的XML配置文件的各个节点和属性的含义。
这里我要稍稍总结一下,spring对bean定义的载入有很多种方式,读取的过程是可插拔的,不论何种形式,spring的IOC容器只要获得了bean定义信息,都可以正常工作。而我们熟知的配置读取方式就是XML文件,如果你希望,可以自己定制配置信息的读取过程,有时间我会研究下spring留给我们扩展的接口在哪里。只要找到了这个入口,那么读取配置信息就任由我们宰割了。
相关文章推荐
- spring源码学习之路---IOC容器初始化要义之bean定义载入(四)
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- spring源码学习之路---IOC容器初始化要义之bean定义载入
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- Spring源码学习IOC(3):IoC容器载入Bean定义资源文件
- Spring源码阅读之IoC容器初始化2 -- BeanDefinition载入与解析
- Spring源码分析1--IoC容器载入Bean定义资源
- 初始化IoC容器(Spring源码阅读)-我们到底能走多远系列(31)
- 【spring源码学习】spring的IOC容器在初始化bean过程
- spring技术内幕3-IOC容器载入Bean定义资源
- Spring应用、原理以及粗读源码系列(一)--框架总述、以Bean为核心的机制(IoC容器初始化以及依赖注入)
- 【Spring Framework 深入】—— IoC容器初始化 -> Bean定义资源的载入解析
- Spring源码阅读(一):IOC容器的初始化
- Spring源码-IOC容器(五)-Bean的初始化
- 初始化IoC容器(Spring源码阅读)
- Spring源码学习IOC(4):IoC容器解析Bean定义资源并注册解析后的Bean
- Spring源码阅读(二)—IOC容器初始化
- Spring源码阅读之IoC容器初始化3 -- BeanDefinition在IoC容器中的注册