博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Spring源码分析系列】加载Bean
阅读量:6477 次
发布时间:2019-06-23

本文共 13104 字,大约阅读时间需要 43 分钟。

1 /** 2      * Create a new XmlBeanFactory with the given input stream, 3      * which must be parsable using DOM. 4      * @param resource XML resource to load bean definitions from 5      * @param parentBeanFactory parent bean factory 6      * @throws BeansException in case of loading or parsing errors 7      */ 8     public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { 9         super(parentBeanFactory);10         this.reader.loadBeanDefinitions(resource);11     }

整个处理过程:

1)封装资源文件

当进入XmlBeanDefinitionReader后首先对参数Resource使用EncodeResource类进行封装

2)获取输入流

从Resource中获取对应的InputSteam并构造InputSource

3)通过构造的InputSource实例和Resource实例继续调用doLoadBeanDefinitions.

loadBeanDefinition的实现:

1 /** 2      * Load bean definitions from the specified XML file. 3      * @param resource the resource descriptor for the XML file 4      * @return the number of bean definitions found 5      * @throws BeanDefinitionStoreException in case of loading or parsing errors 6      */ 7     @Override 8     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { 9         return loadBeanDefinitions(new EncodedResource(resource));10     }

new EncodeResource(resource)实现:

1 private EncodedResource(Resource resource, String encoding, Charset charset) {2         super();3         Assert.notNull(resource, "Resource must not be null");4         this.resource = resource;5         this.encoding = encoding;6         this.charset = charset;7     }
1 /** 2      * Open a {
@code java.io.Reader} for the specified resource, using the specified 3 * {
@link #getCharset() Charset} or {
@linkplain #getEncoding() encoding} 4 * (if any). 5 * @throws IOException if opening the Reader failed 6 * @see #requiresReader() 7 * @see #getInputStream() 8 */ 9 public Reader getReader() throws IOException {10 if (this.charset != null) {11 return new InputStreamReader(this.resource.getInputStream(), this.charset);12 }13 else if (this.encoding != null) {14 return new InputStreamReader(this.resource.getInputStream(), this.encoding);15 }16 else {17 return new InputStreamReader(this.resource.getInputStream());18 }19 }

真正的数据准备阶段:

/**     * Load bean definitions from the specified XML file.     * @param encodedResource the resource descriptor for the XML file,     * allowing to specify an encoding to use for parsing the file     * @return the number of bean definitions found     * @throws BeanDefinitionStoreException in case of loading or parsing errors     */    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
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 {
//从encodeResource中获取已经封装的Resource对象并再次从Resource中获取其中的inputStream InputStream inputStream = encodedResource.getResource().getInputStream(); try {
//InputResource这个类不来自与Spring 全类路径是org.xml.sax.InputSource 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(); } } }

首先对传入的resource参数做封装,目的是考虑到Resource可能存在的编码要求的情况,其次,通过SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据通过参数传入真正的核心处理部分doLoadBeanDefinitions(inputSource,encodeResource.getResource()).

1 /** 2      * Actually load bean definitions from the specified XML file. 3      * @param inputSource the SAX InputSource to read from 4      * @param resource the resource descriptor for the XML file 5      * @return the number of bean definitions found 6      * @throws BeanDefinitionStoreException in case of loading or parsing errors 7      * @see #doLoadDocument 8      * @see #registerBeanDefinitions 9      */10     protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)11             throws BeanDefinitionStoreException {12         try {
//加载XML文件,并得到对应的Document13 Document doc = doLoadDocument(inputSource, resource); //根据返回的Document注册Bean信息14 return registerBeanDefinitions(doc, resource);15 }16 catch (BeanDefinitionStoreException ex) {17 throw ex;18 }19 catch (SAXParseException ex) {20 throw new XmlBeanDefinitionStoreException(resource.getDescription(),21 "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);22 }23 catch (SAXException ex) {24 throw new XmlBeanDefinitionStoreException(resource.getDescription(),25 "XML document from " + resource + " is invalid", ex);26 }27 catch (ParserConfigurationException ex) {28 throw new BeanDefinitionStoreException(resource.getDescription(),29 "Parser configuration exception parsing XML from " + resource, ex);30 }31 catch (IOException ex) {32 throw new BeanDefinitionStoreException(resource.getDescription(),33 "IOException parsing XML document from " + resource, ex);34 }35 catch (Throwable ex) {36 throw new BeanDefinitionStoreException(resource.getDescription(),37 "Unexpected exception parsing XML document from " + resource, ex);38 }39 }

获取XML的验证模式

DTD和XSD区别

DTD即文档类型定义,是一种XML约束模式语言,是XML文件的验证机制,属于XML文件组成的一部分。DTD是一种保证XML文档格式正确的有效方法,可以通过比较XML和DTD文件来看文档是否符合规范,元素和标签使用是否正确。一个DTD文档包含:元素的定义规则,元素间关系的定义规则,元素可使用的属性,可使用的实体或符号规则。

要使用DTD验证模式的时候需要在XML文件的头部声明,以下是在Spring中使用DTD声明方式的代码:

1 
2

获取Document

1 /** 2      * Load the {
@link Document} at the supplied {
@link InputSource} using the standard JAXP-configured 3 * XML parser. 4 */ 5 @Override 6 public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, 7 ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception { 8 9 DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);10 if (logger.isDebugEnabled()) {11 logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");12 }13 DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);14 return builder.parse(inputSource);15 }

首先创建DocumentBuilderFactory,再通过DocumentBuilderFactory创建DocumentBuilder,进而解析inputSource来返回Document对象。

在上述方法中涉及一个参数EntityResolver何为EntityResolver?如果SAX应用程序需要实现自定义处理外部实体,则必须实现此接口并使用setEntityResolver方法向SAX驱动器注册一个实例,也就是说,对于解析一个XML,SAX首先读取该XML文档的声明,根据声明去寻找相应的DTD定义,以便对文档进行一个严重。默认的寻找规则,即通过网络来下载相应的DTD声明,并进行认证。

EntityResolver的作用是项目本身就可以提供一个如何寻找DTD声明的方法,即由程序来实现寻找DTD声明的过程,比如我们将DTD文件放到项目中某处,在实现时直接将此文档读取并返回给SAX即可。

解析及注册BeanDefinitions

当把文件转换为Document后,接下来的提取及注册Bean就是我们的重头戏,当程序已经拥有XML文档文件的Document实例对象时,就会被引入如下的方法:

1 /** 2      * Register the bean definitions contained in the given DOM document. 3      * Called by {
@code loadBeanDefinitions}. 4 *

Creates a new instance of the parser class and invokes 5 * {

@code registerBeanDefinitions} on it. 6 * @param doc the DOM document 7 * @param resource the resource descriptor (for context information) 8 * @return the number of bean definitions found 9 * @throws BeanDefinitionStoreException in case of parsing errors10 * @see #loadBeanDefinitions11 * @see #setDocumentReaderClass12 * @see BeanDefinitionDocumentReader#registerBeanDefinitions13 */14 public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {15 //使用DefaultBeanDefinitionDocumentReader实例化BeanDefinitionDocumentReader BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); //记录统计前BeanDefinition的加载个数16 int countBefore = getRegistry().getBeanDefinitionCount(); //加载及注册bean17 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); //记录本次加载的BeanDefinition个数18 return getRegistry().getBeanDefinitionCount() - countBefore;19 }

1 /** 2      * Register each bean definition within the given root {
@code
} element. 3 */ 4 protected void doRegisterBeanDefinitions(Element root) { 5 // Any nested
elements will cause recursion in this method. In 6 // order to propagate and preserve
default-* attributes correctly, 7 // keep track of the current (parent) delegate, which may be null. Create 8 // the new (child) delegate with a reference to the parent for fallback purposes, 9 // then ultimately reset this.delegate back to its original (parent) reference.10 // this behavior emulates a stack of delegates without actually necessitating one.11 BeanDefinitionParserDelegate parent = this.delegate;12 this.delegate = createDelegate(getReaderContext(), root, parent);13 14 if (this.delegate.isDefaultNamespace(root)) {
//处理profile属性15 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);16 if (StringUtils.hasText(profileSpec)) {17 String[] specifiedProfiles = StringUtils.tokenizeToStringArray(18 profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);19 if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {20 if (logger.isInfoEnabled()) {21 logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +22 "] not matching: " + getReaderContext().getResource());23 }24 return;25 }26 }27 }28 //解析前处理,留给子类实现29 preProcessXml(root);30 parseBeanDefinitions(root, this.delegate); //解析后处理,留给子类实现31 postProcessXml(root);32 33 this.delegate = parent;34 }

preProcessXml和postProcessXml两个方法是空的,这是模板方法模式,在面向对象设计方法中常说:一个雷要么面向集成的设计,要么就用final修饰,这两个方法正是为子类设计的,如果继承自DefaultBeanDefinitionDocumentReader的子类需要在Bean解析前后做一些处理的话,那么只需要重写这两个方法就可以了。

解析并注册BeanDefinition:

1 /** 2      * Parse the elements at the root level in the document: 3      * "import", "alias", "bean". 4      * @param root the DOM root element of the document 5      */ 6     protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//对beans的处理 7 if (delegate.isDefaultNamespace(root)) { 8 NodeList nl = root.getChildNodes(); 9 for (int i = 0; i < nl.getLength(); i++) {10 Node node = nl.item(i);11 if (node instanceof Element) {12 Element ele = (Element) node;13 if (delegate.isDefaultNamespace(ele)) {
//对bean的处理14 parseDefaultElement(ele, delegate);15 }16 else {
//对bean的处理17 delegate.parseCustomElement(ele);18 }19 }20 }21 }22 else {23 delegate.parseCustomElement(root);24 }25 }

在Spring的XML配置里面有两大类Bean声明,一个是默认的,如:

1 

另一类就是自定义的,如:

1 

这两种方式的读取及解析差别是非常大的,如果采用Spring默认的配置,Spring当然知道该如何做,但是如果是自定义的,那么久需要用户实现一些接口及配置了。对于根节点或者子节点如果是默认命名空间的话则采用parseDefaultElement方法进行解析,否则使用delegate.parseCutomElement方法对自定义命名空间进行解析。而判断是否默认命名空间还是自定义命名空间的方法其实是使用node.getNamespaceURI()获取命名空间,并与Spring的命名空间进行对比,如果一致则是默认,否则是自定义。

转载于:https://www.cnblogs.com/dream-to-pku/p/7591692.html

你可能感兴趣的文章
日本开设无人机专业,打造无人机“人才市场”
查看>>
190行代码实现mvvm模式
查看>>
兼容几乎所有浏览器的透明背景效果
查看>>
Linux VNC server的安装及简单配置使用
查看>>
阿里宣布开源Weex ,亿级应用匠心打造跨平台移动开发工具
查看>>
Android项目——实现时间线程源码
查看>>
招商银行信用卡重要通知:消费提醒服务调整,300元以下消费不再逐笔发送短信...
查看>>
C#_delegate - 调用列表
查看>>
[转]Windows的批处理脚本
查看>>
多维数组元素的地址
查看>>
数据库运维体系_SZMSD
查看>>
js的AJAX请求有关知识总结
查看>>
三分 POJ 2420 A Star not a Tree?
查看>>
修改OBS为仅直播音频
查看>>
OCA读书笔记(3) - 使用DBCA创建Oracle数据库
查看>>
Python基础进阶之路(一)之运算符和输入输出
查看>>
阻塞非阻塞异步同步 io的关系
查看>>
ClickStat业务
查看>>
spring3.0.7中各个jar包的作用总结
查看>>
Windows 10 /win10 上使用GIT慢的问题,或者命令行反应慢的问题
查看>>