zoukankan      html  css  js  c++  java
  • spring源码学习五

    spring在注入bean的时候,可以通过bean.xml来配置,在xml文件中配置bean的属性,然后spring在refresh的时候,会去解析xml配置文件,这篇笔记,主要来记录。xml配置文件的解析过程

      先把测试的代码贴出来吧

      

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd ">
     5 
     6         <bean id="userBean" class="com.luban.springsource.xml.UserBean"></bean>
     7 </beans>
     8 
     9 
    10 
    11 public class XmlBeanTest {
    12     public static void main(String[] args) {
    13         ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
    14         System.out.println(ac.getBean(UserBean.class));
    15     }
    16 }

    UserBean就是一个普通的类,没有加任何注解

    下面我们来一步一步解析源码

       对于Xml格式的,是通过ClassPathXmlApplicationContext来实现的,首先会进入到对应的构造函数中

      

    1 public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
    2     super(parent);
    3     this.setConfigLocations(configLocations);
    4     if (refresh) {
    5         this.refresh();
    6     }
    7 }

      在这段代码中,第三行这里,主要是把当前要解析的xml文件存放到了一个String数组中,在后面解析的时候,会遍历这个数据,挨个解析xml文件

      this.refresh()方法完成了解析和初始化,主要来看这个方法

       

     1 @Override
     2 public void refresh() throws BeansException, IllegalStateException {
     3     synchronized (this.startupShutdownMonitor) {
     4         // Prepare this context for refreshing.
     5         //准备工作包括设置启动时间、是否激活标志位、初始化属性源配置
     6         prepareRefresh();
     7 
     8         // Tell the subclass to refresh the internal bean factory.
     9         /**
    10          * 返回一个factory
    11          * xml格式的配置文件,是在这个方法中扫描到beanDefinitionMap中的
    12          * 在org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)中会创建一个XmlBeanDefinitionReader来解析xml文件
    13          * 会把bean.xml解析成一个InputStream,然后再解析成document格式
    14          * 按照document格式解析,从root节点进行解析,判断root节点是bean?还是beans?还是import等,如果是bean
    15          * 就把解析到的信息包装成beanDefinitionHolder,然后调用DefaultListablebeanFactory的注册方法将bean放到beanDefinitionMap中
    16          */
    17         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    18 
    19         // Prepare the bean factory for use in this context.
    20         //准备工厂
    21         prepareBeanFactory(beanFactory);
    22 
    23         try {
    24             // Allows post-processing of the bean factory in context subclasses.
    25             postProcessBeanFactory(beanFactory);
    26 
    27             // Invoke factory processors registered as beans in the context.
    28             invokeBeanFactoryPostProcessors(beanFactory);
    29 
    30             // Register bean processors that intercept bean creation.
    31             registerBeanPostProcessors(beanFactory);
    32 
    33             // Initialize message source for this context.
    34             //初始化MessageSource组件(该组件在spring中用来做国际化、消息绑定、消息解析)
    35             initMessageSource();
    36 
    37             // Initialize event multicaster for this context.
    38             /**
    39              * 注册一个多事件派发器
    40              * 先从beanFactory获取,如果没有,就创建一个,并将创建的派发器放到beanFactory中
    41              */
    42             initApplicationEventMulticaster();
    43 
    44             // Initialize other special beans in specific context subclasses.
    45             /**
    46              * 这是一个空方法,在springboot中,如果集成了Tomcat,会在这里new Tomcat()
    47              */
    48             onRefresh();
    49 
    50             // Check for listener beans and register them.
    51             /**
    52              * 注册所有的事件监听器
    53              * 将容器中的时间监听器添加到 applicationEventMulticaster 中
    54              *
    55              */
    56             registerListeners();
    57 
    58             // Instantiate all remaining (non-lazy-init) singletons.
    59             /**
    60              * TODO
    61              * 完成对bean的实例化
    62              *
    63              * 主要的功能都在这里面
    64              */
    65             finishBeanFactoryInitialization(beanFactory);
    66 
    67             // Last step: publish corresponding event.
    68             /**
    69              * 当容器刷新完成之后,发送容器刷新完成事件
    70              * publishEvent(new ContextRefreshedEvent(this));
    71              */
    72             finishRefresh();
    73         }
    74 
    75         catch (BeansException ex) {
    76             if (logger.isWarnEnabled()) {
    77                 logger.warn("Exception encountered during context initialization - " +
    78                         "cancelling refresh attempt: " + ex);
    79             }
    80 
    81             // Destroy already created singletons to avoid dangling resources.
    82             destroyBeans();
    83 
    84             // Reset 'active' flag.
    85             cancelRefresh(ex);
    86 
    87             // Propagate exception to caller.
    88             throw ex;
    89         }
    90 
    91         finally {
    92             // Reset common introspection caches in Spring's core, since we
    93             // might not ever need metadata for singleton beans anymore...
    94             resetCommonCaches();
    95         }
    96     }
    97 }

     在前面的spring源码博客中有说到过,spring初始化的一个大致流程是这样的:

      1.把所有的要注入的class解析,存到beanDefinitionMap中

      2.循环遍历beanDefinitionMap,把每个beanDefinition经过初始化,实例化等生命周期方法的处理,转变为spring容器中存储的bean

      3.把bean存到bean的单实例池中

      今天要说的,主要是第一步这里,把class解析到beanDefinition中,对于注解版的配置,是在invokeBeanFactoryPostProcessors(beanFactory);中完成,区分了@ComponentScan、ImportSeletor、ImportBeanDefinitionRegistrar、@Bean这么几种情况

      那么,xml是另外一种方式,主要是在ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();中,完成了对bean.xml的解析

     这个方法,进来之后,首先是刷新beanFactory(),其中,有一个loadBeanDefinitions(beanFactory);方法

     这个方法里面的逻辑还是比较简单的,大致说一下:

      1.new一个xmlBeanDefinitionReader

      2.遍历前面存放的配置文件的数组,依次解析

      3.将xml配置文件转换成inputSource,然后生成一个Document对象; Document doc = this.doLoadDocument(inputSource, resource);  这里的这个方法,就是把当前xml配置文件转解析成一个document对象,对于这个方法,

    我没有怎么深入研究,姑且我们就认为这是一个黑盒方法

      真正开始解析xml,是从这个方法中开始的

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
        int countBefore = this.getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
        return this.getRegistry().getBeanDefinitionCount() - countBefore;
    }

    从这里的registerBeanDefinitions方法开始来解析的


    这里是解析root根节点(beans)之后,会获取到当前root下面的子节点,然后依次循环处理子节点的代码;

     1 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
     2         if (delegate.nodeNameEquals(ele, "import")) {
     3             this.importBeanDefinitionResource(ele);
     4         } else if (delegate.nodeNameEquals(ele, "alias")) {
     5             this.processAliasRegistration(ele);
     6         } else if (delegate.nodeNameEquals(ele, "bean")) {
     7             this.processBeanDefinition(ele, delegate);
     8         } else if (delegate.nodeNameEquals(ele, "beans")) {
     9             this.doRegisterBeanDefinitions(ele);
    10         }
    11 
    12     }

    从上面说的registerBeanDefinitions到这里的解析方法中间没有几步,不细说了,直接把调用链给出来,可以自己debug再细看一下

    我们来说解析的这个方法:

     会依次判断beans子节点是哪几个,由于我们只配置了一个<bean,所以,来看处理bean的方法

    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
    bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

    try {
    BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
    } catch (BeanDefinitionStoreException var5) {
    this.getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, var5);
    }

    this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }

    }
    在处理<bean>的时候,会返回一个BeanDefinitionHolder对象,在解析注解版的class的时候,也有好多地方是这样用的,把class解析成BeanDefinitionHolder,然后再把beanDefinitionHolder对应的BeanName,作为BeanDefinitionMap的key,
    holder里面的beanDefinition作为value;这里来着重看parseBeanDefinitionElement()方法,因为经过这个方法之后,已经解析成beanDefinition了

    @Nullable
    public AbstractBeanDefinition parseBeanDefinitionElement(
    Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));

    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    //获取xml中bean节点配置的class(就是类的全类名)
    className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }

    try {
    //根据全类名,通过反射
    AbstractBeanDefinition bd = createBeanDefinition(className, parent);

    //解析<bean>节点的属性信息,比如:lazy/autowire/scope/destroy-method/init-method等,然后把解析到的属性set到beanDefinition中
    parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    //解析bean的子节点 <description>的信息
    bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

    parseMetaElements(ele, bd);
    //下面两个方法分别解析<bean>的子节点 lookup-method和replaced-method
    parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

    //解析bean的构造函数,最后调用的是bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder);
    parseConstructorArgElements(ele, bd);
    parsePropertyElements(ele, bd);
    parseQualifierElements(ele, bd);

    bd.setResource(this.readerContext.getResource());
    bd.setSource(extractSource(ele));

    return bd;
    }
    catch (ClassNotFoundException ex) {
    error("Bean class [" + className + "] not found", ele, ex);
    }
    catch (NoClassDefFoundError err) {
    error("Class that bean class [" + className + "] depends on not found", ele, err);
    }
    catch (Throwable ex) {
    error("Unexpected failure during bean definition parsing", ele, ex);
    }
    finally {
    this.parseState.pop();
    }

    return null;
    }
    这里面,加了一些注释,不细看每个方法了,其实就是解析每个配置的信息,然后把属性信息set到beanDefinition中


    总结
    1.首先把入参中待解析的xml文件存到了一个string数组中
    2.依次遍历数组,来解析
    3.解析的时候,会把xml文件解析成document对象,然后首先从根节点<beans>开始解析
    4.解析到<bean>节点的时候,会依次解析bean的属性信息以及bean的子节点信息,把解析到的属性set到beanDefinition中
    5.把beanDefinition再put到beanDefinitionMap中
  • 相关阅读:
    文件数据分析制作过程【1】
    VBA学习(4)
    QTP9.2 .net与java插件破解
    GPRS开发系列文章之实战篇
    time_t到.NET的转换
    RAS函数
    GPRS开发系列文章之入门篇
    [转]惹恼程序员的十件事
    好玩和可爱的网站
    GPRS开发系列文章之进阶篇
  • 原文地址:https://www.cnblogs.com/mpyn/p/12001391.html
Copyright © 2011-2022 走看看