• 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抽象类所定义的 

  • 相关阅读:
    结构体和枚举
    [转载]玩转Asp.net MVC 的八个扩展点
    SQLServer处理亿万级别的数据的优化措施
    Lambda表达式
    匿名类型
    单例模式——懒汉式和饿汉式详解
    Cglib动态代理实现原理
    集合的子集
    深入剖析ThreadLocal
    java注解的自定义和使用
  • 原文地址:https://www.cnblogs.com/lxjshuju/p/7092925.html
Copyright © 2020-2023  润新知