接上篇【】 ,我们看到BeanDefinitionReader解决的是从资源文件(xml,propert)到BeanDefinition集合的过程。所以BeanDefinitionReader接口有两个实现版本。

 

  BeanDefinitionReader的接口声明,ResourceLoader是spring中解决Resource加载的操作。四个loadBeanDefinitions就是重载解决单个或者多个资源文件的处理问题。

  

  loadBeanDefinitions 是加载BeanDefinition接口的核心入口,AbstractBeanDefinitionReader中剩下了loadBeanDefinitions(Resource resource)这个方法给子类实现。

  开始加载资源文件,这一步会将Resource构造为一个EncodedResource,添加了编码的相关控制信息。

 //XmlBeanDefinitionReader.java
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}

  下一步需要去判断当前配置文件是否被循环加载。

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());
}
//没有明白为什么需要使用ThreadLoacal?
//private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(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();
}
}
}

   真正执行加载操作的是doLoadBeanDefinitions方法。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
       //获取xml的校验格式,dtd或者xsd的校验模式
int validationMode = getValidationModeForResource(resource);
       //解析xml为dom,用了sax解析的
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
       //真正的主角来了
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解析xml文件为org.w3c.dom.Document使用了SAX的实现方法,除了SAX,常用的xml解析方法还有dom,dom4j,jdom等方法。

  registerBeanDefinitions是正式注册BeanDefinition的地方,这里又引入了一个新的接口BeanDefinitionDocumentReader(定义如何解析dom为BeanDefinition)。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     //创建一个解析器
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
     //设置上下文环境
documentReader.setEnvironment(this.getEnvironment());
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}

    解析的实现为reader.registerBeanDefinitions方法。

    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
logger.debug("Loading bean definitions");
Element root = doc.getDocumentElement();
doRegisterBeanDefinitions(root);
}

  缓存readerContext,目的不清楚。直接parse

    protected void doRegisterBeanDefinitions(Element root) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!this.environment.acceptsProfiles(specifiedProfiles)) {
return;
}
} // Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createHelper(this.readerContext, root, parent); preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root); this.delegate = parent;
}

  preProcessXml和postProcessXml方法暂时留空,目的为了需要增加新的实现版本时候方便扩展。

protected void preProcessXml(Element root) {
}
protected void postProcessXml(Element root) {
}

  parseBeanDefinitions方法内将

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
     //判断解析的BeanDefinition是spring定义的还是自定义扩展的
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定义的BeanDefinition的解析以beans , bean , import , alias开头的四种bean,其他标示开始认为是自定义bean。自定义bean的解析机制都要从NamespaceHandler接口出发。Spring-core和Spring-beans包内就提供了如下这些实现。

  

    下面是BeanDefinitionParserDelegate类中具体处理自定义bean的代码,允许根据不同的nameSpaceUri查找当前应用下面定义的解析类

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
String namespaceUri = getNamespaceURI(ele);
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}

  至此解析部分完成了。但是解析完成的BeanDefinition放到哪里了呢?看一个例子:

  

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
         //真正执行注册BeanDefinition的类
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}

  这样解析加载注册BeanDefinition 的任务就结束了。

Spring源码入门——XmlBeanDefinitionReader解析的更多相关文章

  1. Spring源码入门——DefaultBeanNameGenerator解析 转发 https://www.cnblogs.com/jason0529/p/5272265.html

    Spring源码入门——DefaultBeanNameGenerator解析   我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指 ...

  2. Spring源码入门——AnnotationBeanNameGenerator解析

    ---恢复内容开始--- 接上篇,上篇解析了DefaultBeanGenerator生成bean name的过程(http://www.cnblogs.com/jason0529/p/5272265. ...

  3. Spring源码入门——DefaultBeanNameGenerator解析

    我们知道在spring中每个bean都要有一个id或者name标示每个唯一的bean,在xml中定义一个bean可以指定其id和name值,但那些没有指定的,或者注解的spring的beanname怎 ...

  4. 从零开始学spring源码之xml解析(一):入门

    谈到spring,首先想到的肯定是ioc,DI依赖注入,aop,但是其实很多人只是知道这些是spring核心概念,甚至不知道这些代表了什么意思,,作为一个java程序员,怎么能说自己对号称改变了jav ...

  5. 从零开始学spring源码之xml解析(二):默认标签和自定义标签解析

    默认标签: 上一篇说到spring的默认标签和自定义标签,发现这里面东西还蛮多的.决定还是拆开来写.今天就来好好聊聊这两块是怎么玩的,首先我们先看看默认标签: private void parseDe ...

  6. Spring源码-入门

    一.测试类 public class Main { public static void main(String[] args) { ApplicationContext applicationCon ...

  7. Spring源码之XmlBeanDefinitionReader与Resource

    一.DefaultListableBeanFactory类, 里面有一个成员变量beanDefinitionMap,Bean定义对象的Map, BeanDefinition就对应XML的属性配置 /* ...

  8. Spring源码解析02:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

  9. Spring源码解析 | 第二篇:Spring IOC容器之XmlBeanFactory启动流程分析和源码解析

    一. 前言 Spring容器主要分为两类BeanFactory和ApplicationContext,后者是基于前者的功能扩展,也就是一个基础容器和一个高级容器的区别.本篇就以BeanFactory基 ...

随机推荐

  1. 如何组织css,写出高质量的css代码

    !如何组织css一:css的API 属于基础部分,这部分的能力用“对”和“错”来评判. 比如说把文字设置为红色,只能用color:red:这种写法是对的,其他任何写法都是错的. 二:css框架 不能用 ...

  2. 图片上没有line-height垂直居中

    <style> div {     width: 150px;     height: 155px;     line-height: 155px;     border: 1px sol ...

  3. Oracle删除表、字段之前判断表、字段是否存在

    这篇文章主要介绍了Oracle删除表.字段之前判断表.字段是否存在的相关资料,需要的朋友可以参考下 在Oracle中若删除一个不存在的表,如 “DROP TABLE tableName”,则会提示: ...

  4. IntelliJ IDEA 13怎么创建JAVA SE项目

    如下图,直接下一步,如果需要的话可以选择建立Main函数:

  5. NSMutableArray,NSMutableDictionary的内存管问题

    今天做项目遇到一个问题,在一个类中定义了一个可变数组,使用的是copy的内存管理策略 当往数组中添加包装好的基本数据的时候,程序直接崩溃了.解决方法:把copy换成strong就不会崩溃了; 后来做了 ...

  6. 保护模式下pmtest1.asm的理解

    整个代码对应内存线性地址分为四段,[gdt] [code32] [video32] [code16] 代码先在实模式[code16]下运行,code16中的cs就是系统分配的该程序物理地址的基址. 编 ...

  7. 2013年最受欢迎的16个HTML5 WordPress主题

    本文中,我整理了16个最新.最受欢迎的WordPress主题,它们一致的特征是支持HTML5, 它们秉承最佳设计理念以及最新的流行趋势,将它们归纳与此,希望对喜欢HTML5,准备用WordPress做 ...

  8. EasyUI中datagrid的行编辑模式中,找到特定的Editor,并为其添加事件

    有时候在行编辑的时候,一个编辑框的值要根据其它编辑框的值进行变化,那么可以通过在开启编辑时,找到特定的Editor,为其添加事件 // 绑定事件, index为当前编辑行 var editors = ...

  9. 【转】Xcode重构功能怎么用我全告诉你

    原文网址:http://www.cocoachina.com/ios/20160127/15097.html 你会经常需要重构你的代码,让它有更好的结构,可读性或者提高可维护性.Xcode作为IDE其 ...

  10. CSS学习笔记——定位position属性的学习

    今天学习之前剩下的一个问题:CSS的position属性.首先归纳出和position相关的问题: position作为一个属性,它一共有哪几个属性值? position常用的属性值有哪几个?分别有什 ...