Spring源码解析和配置文件加载

Spring类的继承结构图:


Spring运用了大量的模板方法模式和策略模式,所以各位看源码的时候,务必留意,每一个继承的层次都有不同的作用,然后将相同的地方抽取出来,依赖抽象将不同的处理按照不同的策略去处理。

步骤A. 读取 Resource 文件形成 Document 模型 


    类图: XmlBeanFactory -> XmlBeanDefinitionReader 
    
    Spring 使用 XmlBeanDefinitionReader 来读取并解析 xml 文件,XmlBeanDefinitionReader 是 BeanDefinitionReader 接口的实现。 
    BeanDefinitionReader 定义了 Spring 读取 Bean 定义的一个接口,这个接口中有一些 loadBeanDefinitions 方法, 用于读取 Bean 配置。 
    BeanDefinitionReader 接口有两个具体的实现,其中之一就是从 Xml 文件中读取配置的 XmlBeanDefinitionReader,另一个则是从 Java Properties 文件中读取配置的PropertiesBeanDefinitionReader。
    (注:开发人员也可以提供自己的 BeanDefinitionReader 实现,根据自己的需要来读取 spring bean 定义的配置。) 

步骤B. 解析 Document 得到 Bean 配置 

    类图: XmlBeanDefinitionReader-> BeanDefinitionDocumentReader 

    BeanDefinitionDocumentReader 接口中只定义了一个方法 registerBeanDefinitions. 有一个默认实现 DefaultBeanDefinitionDocumentReader. 
    DefaultBeanDefinitionDocumentReader 主要完成两件事情,解析 <bean> 元素,为扩展 spring 的元素寻找合适的解析器,并把相应的元素交给解析器解析。 

过程: 
    在 XmlBeanFactory 中创建了 XmlBeanDefinitionReader 的实例,并在 XmlBeanFactory 的构造方法中调用了 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法,由 loadBeanDefinitions 方法负责加载 bean 配置并把 bean 配置注册到 XmlBeanFactory 中。 
    在 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法中, 调用 DefaultDocumentLoader 的 loadDocument 读取配置文件为 Document, 然后调用 BeanDefinitionDocumentReader 的 registerBeanDefinitions 方法 来解析 Bean. 

源码解析: 

在XmlBeanFactory初始化时, 需要指定Resource对象. 
Java代码   收藏代码
  1. public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)  
  2.     throws BeansException  
  3. {  
  4.     super(parentBeanFactory);  
  5.     reader = new XmlBeanDefinitionReader(this);  
  6.     reader.loadBeanDefinitions(resource);  
  7. }  

1. 先来分析下XmlBeanDefinitionReader这个类. 
Java代码   收藏代码
  1. public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader  

接着 
Java代码   收藏代码
  1. public abstract class AbstractBeanDefinitionReader  
  2.     implements BeanDefinitionReader  

再继续 
Java代码   收藏代码
  1. public interface BeanDefinitionReader  

在BeanDefinitionReader中定义有许多loadBeanDefinitions方法 
Java代码   收藏代码
  1. public abstract int loadBeanDefinitions(Resource resource)  
  2.     throws BeanDefinitionStoreException;  
  3.   
  4. public abstract int loadBeanDefinitions(Resource aresource[])  
  5.     throws BeanDefinitionStoreException;  
  6.   
  7. public abstract int loadBeanDefinitions(String s)  
  8.     throws BeanDefinitionStoreException;  
  9.   
  10. public abstract int loadBeanDefinitions(String as[])  
  11.     throws BeanDefinitionStoreException;  

来回头看XmlBeanDefinitionReader对loadBeanDefinitions方法的实现 
在loadBeanDefinitions方法中调用了doLoadBeanDefinitions方法, 跟踪doLoadBeanDefinitions方法 
Java代码   收藏代码
  1. Document doc = documentLoader.loadDocument(inputSource, getEntityResolver(), errorHandler, validationMode, isNamespaceAware());  

通过一个叫documentLoader的东西的loadDocument方法来加载配置文件形成DOM, 来看看documentLoader 
Java代码   收藏代码
  1. private DocumentLoader documentLoader  
  2. ...  
  3. documentLoader = new DefaultDocumentLoader();  

跟踪到DefaultDocumentLoader 
Java代码   收藏代码
  1. public class DefaultDocumentLoader  
  2.     implements DocumentLoader  
  3. ...  
  4.     public Document loadDocument(InputSource inputSource, EntityResolver entityResolver, ErrorHandler errorHandler, int validationMode, boolean namespaceAware)  
  5.         throws Exception  
  6.     {  
  7.         DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);  
  8.         if(logger.isDebugEnabled())  
  9.             logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");  
  10.         DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);  
  11.         return builder.parse(inputSource);  
  12.     }  
  13. ...  

哦哦, 我们知道了, 是通过sax解析得到Dom的, 至于怎么解析, 不属于Spring范畴, 不做研究. 

在这一步, 已完成了从配置文件读取到Domcument. 接着要开始解析Dom了  

再继续, 解析成Dom后接着调用了registerBeanDefinitions方法 
Java代码   收藏代码
  1. return registerBeanDefinitions(doc, resource);  

来看看registerBeanDefinitions的实现 
Java代码   收藏代码
  1. ...  
  2. BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();  
  3. int countBefore = getRegistry().getBeanDefinitionCount();  
  4. documentReader.registerBeanDefinitions(doc, createReaderContext(resource));  
  5. return getRegistry().getBeanDefinitionCount() - countBefore;  
  6. ...  

在这里, 有一个BeanDefinitionDocumentReader接口, 实际上Spring对它有一个默认的实现类叫DefaultBeanDefinitionDocumentReader, 来看看它的家族 
Java代码   收藏代码
  1. public class DefaultBeanDefinitionDocumentReader   

Java代码   收藏代码
  1. public interface BeanDefinitionDocumentReader  

BeanDefinitionDocumentReader只有一个registerBeanDefinitions方法 
Java代码   收藏代码
  1. public abstract void registerBeanDefinitions(Document document, XmlReaderContext xmlreadercontext)  
  2.     throws BeanDefinitionStoreException;  

该方法需要两个参数, 一个是Document模型,这个应该是我们读取配置文件获取到的, 另一个是XmlReaderContext对象, 我们在上面方法中看到是通过createReaderContext(resource)得到的, 那就看看具体如何得到 
Java代码   收藏代码
  1. protected XmlReaderContext createReaderContext(Resource resource)  
  2. {  
  3.     if(namespaceHandlerResolver == null)  
  4.         namespaceHandlerResolver = createDefaultNamespaceHandlerResolver();  
  5.     return new XmlReaderContext(resource, problemReporter, eventListener, sourceExtractor, this, namespaceHandlerResolver);  
  6. }  

能过构造函数new出来的, 且有一个重要参数resource 
再继续来看DefaultBeanDefinitionDocumentReader对BeanDefinitionDocumentReader的registerBeanDefinitions方法实现 
Java代码   收藏代码
  1.   public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)  
  2.   {  
  3.       ...  
  4. BeanDefinitionParserDelegate delegate = createHelper(readerContext, root);  
  5. ...  
  6.       parseBeanDefinitions(root, delegate);  
  7.       ...  
  8.   }  

嘿嘿, 开始解析Dom了哦, 其中主要是parseBeanDefinitions方法, 来看看具体是如何解析的 
Java代码   收藏代码
  1. protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate)  
  2. {  
  3.     if(delegate.isDefaultNamespace(root.getNamespaceURI()))  
  4.     {  
  5.         NodeList nl = root.getChildNodes();  
  6.         for(int i = 0; i < nl.getLength(); i++)  
  7.         {  
  8.             org.w3c.dom.Node node = nl.item(i);  
  9.             if(node instanceof Element)  
  10.             {  
  11.                 Element ele = (Element)node;  
  12.                 String namespaceUri = ele.getNamespaceURI();  
  13.                 if(delegate.isDefaultNamespace(namespaceUri))  
  14.                     parseDefaultElement(ele, delegate);  
  15.                 else  
  16.                     delegate.parseCustomElement(ele);  
  17.             }  
  18.         }  
  19.   
  20.     } else  
  21.     {  
  22.         delegate.parseCustomElement(root);  
  23.     }  
  24. }  

看到了吧, 循环解析Domcument节点 
parseDefaultElement方法和delegate的parseCustomElement方法 
先来看parseDefaultElement方法 
Java代码   收藏代码
  1. private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)  
  2. {  
  3.     if(DomUtils.nodeNameEquals(ele, "import"))  
  4.         importBeanDefinitionResource(ele);  
  5.     else  
  6.     if(DomUtils.nodeNameEquals(ele, "alias"))  
  7.         processAliasRegistration(ele);  
  8.     else  
  9.     if(DomUtils.nodeNameEquals(ele, "bean"))  
  10.         processBeanDefinition(ele, delegate);  
  11. }  

看到这就很清楚了, 就是根据节点的名称作不同解析, 如我们Spring配置文件中常有以下几种配置 
Java代码   收藏代码
  1. <import resource="classpath:xxx" />  
  2. <bean id="xxx" class="xxx.xxx.xxx" />  
  3. <alias name="xxxx" alias="yyyyy"/>  

对<import>节点, 调用importBeanDefinitionResource方法解析, 此方法中, 又回到第一步读取配置文件并解析. 如此递归循环. 
Java代码   收藏代码
  1. ...  
  2. Resource relativeResource = getReaderContext().getResource().createRelative(location);  
  3. int importCount = getReaderContext().getReader().loadBeanDefinitions(relativeResource);  
  4. ...  

对<alias>节点, 调用processAliasRegistration进行别名解析 
我们主要看对<bean>节点调用processBeanDefinition进行解析 
Java代码   收藏代码
  1. protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate)  
  2. {  
  3.     BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);  
  4.     if(bdHolder != null)  
  5.     {  
  6.         bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);  
  7.         try  
  8.         {  
  9.             BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());  
  10.         }  
  11.         catch(BeanDefinitionStoreException ex)  
  12.         {  
  13.             getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);  
  14.         }  
  15.         getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));  
  16.     }  
  17. }  

嘿嘿, 又用到delegate对象了, 且调用它的BeanDefinitionHolder方法, 返回一个BeanDefinitionHolder, 进去看它的parseBeanDefinitionElement方法 
Java代码   收藏代码
  1. public class BeanDefinitionParserDelegate  
  2. {     
  3.     private final Set usedNames = new HashSet();  
  4.     ...  
  5.     public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean)  
  6.     {  
  7.         ... 解析id, name等属性, 并验证name是否唯一, 并将name保存在usedNames中  
  8.         AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);  
  9.         ...   
  10.         return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);  
  11.     }  
  12.     ...  
  13. }  

可以看到, 在BeanDefinitionHolder中保存了BeanDefinition的定义 
OK, 重头戏开始, 最经典的部分出现了, 请看parseBeanDefinitionElement方法 
Java代码   收藏代码
  1. public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, BeanDefinition containingBean)  
  2. {  
  3.     ...  
  4.     代码太长, 请参考具体代码  
  5.     AbstractBeanDefinition bd = BeanDefinitionReaderUtils.createBeanDefinition(parent, className, readerContext.getBeanClassLoader());  
  6.     ...  
  7.     return abstractbeandefinition;  
  8.     ...  
  9. }  

在这个方法中, 解析了bean的所有属性, 有最常用的class, scope, lazy-init等等. 并返回一个AbstractBeanDefinition实例. 至于具体怎么解析, 就只能进一步跟踪了, 不过既然到了这一步, 已经明白了它的基本原理, 具体实现就不作介绍 

这一步将节点解析成BeanDefinitionHolder对象, 再看看如何注册, 回到DefaultBeanDefinitionDocumentReader的processBeanDefinition方法 
看到对解析到的bdHolder对象又做了decorateBeanDefinitionIfRequired操作, 来看看实现 
... 暂留空 

接着调用了BeanDefinitionReaderUtils的registerBeanDefinition方法注册bdHolder, 来看看如何实现的 
Java代码   收藏代码
  1. public class BeanDefinitionReaderUtils  
  2. {  
  3.     public static void registerBeanDefinition(BeanDefinitionHolder bdHolder, BeanDefinitionRegistry beanFactory)  
  4.         throws BeanDefinitionStoreException  
  5.     {  
  6.         String beanName = bdHolder.getBeanName();  
  7.         beanFactory.registerBeanDefinition(beanName, bdHolder.getBeanDefinition());  
  8.         String aliases[] = bdHolder.getAliases();  
  9.         if(aliases != null)  
  10.         {  
  11.             for(int i = 0; i < aliases.length; i++)  
  12.                 beanFactory.registerAlias(beanName, aliases[i]);  
  13.   
  14.         }  
  15.     }  
  16. }  

看吧, 又调用了BeanDefinitionRegistry的registerBeanDefinition方法, 跟踪之 (这个要看DefaultListableBeanFactory的实现) 
Java代码   收藏代码
  1. public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory  
  2.     implements ConfigurableListableBeanFactory, BeanDefinitionRegistry  
  3. {  
  4.     private final Map beanDefinitionMap;  
  5.     private final List beanDefinitionNames;  
  6.     ...  
  7.     public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)  
  8.         throws BeanDefinitionStoreException  
  9.     {  
  10.         ...  
  11.         Object oldBeanDefinition = beanDefinitionMap.get(beanName);  
  12.         ...  
  13.         beanDefinitionMap.put(beanName, beanDefinition);  
  14.         ...  
  15.     }  
  16. }  

这里, 看到了一个最最重要的对象就是beanDefinitionMap, 这个map存放了所有的bean对象, 和我们通常讲的容器概念最为接近, getBean时实际是也是从这里辚取对象, 相同的还有一个beanDefinitionNames, 但这个只保存bean的名称 
完成上面之后, 还有一步操作beanFactory.registerAlias(beanName, aliases[i]); 
这个实现实际是上AbstractBeanFactory抽象类所定义的 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值