zoukankan      html  css  js  c++  java
  • 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分:

    一、IOC的初始化过程,结合代码和debug过程重点说明

    1、 为什么要debug?

    答:直接自己从源码看第一遍,会有一个初步的认识;但是看完之后,会因为没有实际走一遍执行而无法验证自己认知的过程;另外就是:因为基于接口本身会有多个实现,因此在很多情况下,通过Ctrl+B直接会进入到接口中,但是方法的实现体为空,通过Ctrl+Alt+B查看具体实现,会出现多个类都实现了这个方法,具体是哪一个,无法准确确定,容易导致理解失误

    2、 初始化的阶段及对应调用到的方法

    从SpringMVC的demo中的web.xml配置项可以看到:

    <listener>
        <listener-class>

                   org.springframework.web.context.ContextLoaderListener

    </listener-class>
    </listener>

    从ContextLoaderListener进入,这是入口,之后会经过refresh()过程,refresh()过程中会将IOC容器初始化完成,其中重点有:load阶段、process阶段、register阶段;接下来会从入口以及后续的三个阶段进行介绍。

    二、详解

    入口部分:

    进入到ContextLoaderListener类,ContextLoaderListener实现了ServletContextListener接口,该接口中有两个default方法,分别是:

    //接收Web应用程序初始化过程正在启动的通知

    default public void contextInitialized(ServletContextEvent sce) {}

    //接收ServletContext即将关闭的通知

    default public void contextDestroyed(ServletContextEvent sce) {}

    ContextLoaderListener类中未保持原有的default,重新做了实现:

    /**
     * Initialize the root web application context.
     */
    @Override
    public void contextInitialized(ServletContextEvent event) {
       initWebApplicationContext(event.getServletContext());
    }

    然后进入到父类ContextLoader类的initWebApplicationContext方法中,该方法中会继续通过:configureAndRefreshWebApplicationContext

    最终调用到:AbstractApplicationContext中的refresh(),至此进入到IOC的load阶段(下方为abstractApplicationContext的类图)

    load过程:refresh()

    进入到重要逻辑分析 ——》ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

    一步步往里走,到AbstractRefreshableApplicationContext中,具体如下:
    protected final void refreshBeanFactory() throws BeansException {
       //判断是否已经有beanFactory了,有则销毁
       if (hasBeanFactory()) {
          //销毁Beans
          destroyBeans();
          //将BeanFactory的所有相关内容全部置为null,其中包含:
          //serializedID为null,并且对象引用本身置为null
          //即等GC了 
          closeBeanFactory();
       }
       try {
          //创建BeanFactory
          DefaultListableBeanFactory beanFactory = createBeanFactory();
          beanFactory.setSerializationId(getId());
          customizeBeanFactory(beanFactory);
          //加载BeanDefinitions
          loadBeanDefinitions(beanFactory);
          synchronized (this.beanFactoryMonitor) {
             this.beanFactory = beanFactory;
          }
       }
       catch (IOException ex) {
          throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
       }
    } 

    从这里的createBeanFactory()跟进去可以进入看到是new了一个DefaultListableBeanFactory

    DefaultListableBeanFactory的类图如下:

    之后下一步loadBeanDefinitions(beanFactory)——》XmlWebApplicationContext类的loadBeanDefinitions

    中(这里就是会有多个实现的情况出现,直接通过demo单步调试跟进确定程序走向),之后会new一个XmlBeanDefinitionReader实例,是以前面new出来的beanFactory为参数,这个xmlReaderDefinitionReader会作为参数传入到loadBeanDefinitions(XmlBeanDefinitionReader reader)方法中,之后一级一级进行转换,最终进入到:AbstractBeanDefinitionReader类中的LoadDefinitions,这个时候参数经过了前面的各种过程的转换,最终进入到了doLoadBeanDefinitions(inputSource, encodedResource.getResource())

    这里做了层层封装,每一次从外层进入到里层的具体实现和调用上,主要变化的是:参数形式,可以看到:从getConfigLocations()得到配置文件的路径,默认路径是:

    /** Default config location for the root context. */
    public static final String DEFAULT_CONFIG_LOCATION = "/WEB-INF/applicationContext.xml";

    再到基于得到的配置路径,对其进行循环loaderDefinitions,在每一层循环中,是针对单一的一个String configLocation,之后将String类型的location转换成Resource,再转换成InputStream,之后转成InputSource,最终在doLoadBeanDefinitions中将直接用InputSource的实例作为参数,层层递进层层转换:

    //真正从特定XML文件中加载bean definitions的方法(这里将exception的内容没有全部放进来)
    
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    
          throws BeanDefinitionStoreException {
    
    
    
       try {
    
          //加载————》得到一个doc对象。其实最后一个是DOMParser类的对象
    
          //至此,整个过程中不断去将File先解析之后再转换成输入流,再最终得到Document doc,加载完成
    
          Document doc = doLoadDocument(inputSource, resource);
    
          //注册:具体做的事项是:
    
          //1、解析xml
    
          //2、将解析得到的BeanName和beanDefinition都存储到beanDefinitionMap中进行注册
    
          int count = registerBeanDefinitions(doc, resource);
    
          if (logger.isDebugEnabled()) {
    
             logger.debug("Loaded " + count + " bean definitions from " + resource);
    
          }
    
          return count;
    
       }
     

    其中,会先doLoadDocument方法调用得到一个DomParser的对象;至此加载完成;然后进入到registerBeanDefinitions的过程,其中:会有process过程 + register过程

    process过程:

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
       //new一个BeanDefinitionDocumentReader对象
       BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
       int countBefore = getRegistry().getBeanDefinitionCount();
       //注册BeanDefinitions
       //使用documentReader实例调用registerBeanDefinitions方法,具体操作内容包含:
       //(1)BeanDefinitionDocumentReader处理Document元素时,将Document元素的解析工作委托给
       // BeanDefinitionParserDelegate处理,其中会将beanName做一个唯一值的转换,为后续的register到map中提供前提
       //(2)判断BeanDefinitionMap中是否有某个k,v已经存在,有的话更新Definition的v值,无则直接put k,v
       //DefaultBeanDefinitionDocumentReader 实现了 BeanDefinitionDocumentReader接口
       documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
       return getRegistry().getBeanDefinitionCount() - countBefore;
    }

    其中process过程在registetBeanDefinitions跟进到下一步中,即:DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法,看到:doRegisterBeanDefinitions方法,看到这里就可以兴奋一下啦,真正做事情的来了:

    其中关键步骤就两部分:第一部分是createDelegate并赋值给delegate字段;之后进行processxml三步曲 preprocess、parse、postprocess

    protected void doRegisterBeanDefinitions(Element root) {
          //先赋值给parent,之后创建createDelegate重新赋值给delegate
       BeanDefinitionParserDelegate parent = this.delegate;
    
       //在DefaultBeanDefinitionDocumentReader处理Document元素时,
       // 将Document文档内元素具体解析工作委托给BeanDefinitionParserDelegate类来处理
       this.delegate = createDelegate(getReaderContext(), root, parent);
    
       if (this.delegate.isDefaultNamespace(root)) {
          String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
          if (StringUtils.hasText(profileSpec)) {
             String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                   profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
             // We cannot use Profiles.of(...) since profile expressions are not supported
             // in XML config. See SPR-12458 for details.
             if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                if (logger.isDebugEnabled()) {
                   logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                         "] not matching: " + getReaderContext().getResource());
                }
                return;
             }
          }
       }
    
       //预处理Xml
       preProcessXml(root);
       //解析BeanDefinitions
       parseBeanDefinitions(root, this.delegate);
       //后置处理Xml
       postProcessXml(root);
    
       this.delegate = parent;
    }

    进入到parseBeanDefinitions(root, this.delegate)中,跟到具体实现中:

    //处理给定的bean节点,解析bean definitions,并且通过registry进行注册
    protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
       //这个BeanDefinitionHolder从命名上来看:
       //分析含义是:用来包裹存储一个BeanDefinition的容器,因为叫做Holder
       //实际是:确实是用来存储这个BeanDefinition的包裹,其中对beanName做了一个特定处理,将之转换成了唯一值
    
    //并且进行详细的解析,包含:对于set、list、map等
       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));
       }
    }

    register过程:

    从上面代码中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())会进入到类BeanDefinitionUtils对static方法registerBeanDefinition的调用:

    public static void registerBeanDefinition(
          BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
          throws BeanDefinitionStoreException {
    
       // Register bean definition under primary name.
       String beanName = definitionHolder.getBeanName();
       //根据name进行注册,因为name是唯一性的了
       //具体里面就是直接调用了map.put,将其放入到beanDefinitionMap中
       registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
    
       // Register aliases for bean name, if any.
       String[] aliases = definitionHolder.getAliases();
       if (aliases != null) {
          for (String alias : aliases) {
             registry.registerAlias(beanName, alias);
          }
       }
    }

    之后跟进到registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()) 最终会进入到DefaultListableBeanFactory类中:

    摘取方法中的关键逻辑:

    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
          throws BeanDefinitionStoreException {
    
    //从map中取出beanName的key对应的Definition的对象
    BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
    if (existingDefinition != null) {
    
    }
    //为map中的beanName的key值重新设置对应的v值
    this.beanDefinitionMap.put(beanName, beanDefinition);
    
    }

    其中beanDefintionMap的定义如下:

    /** Map of bean definition objects, keyed by bean name. */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

    二、将以上梳理的整个过程通过时序图的方式,更清晰地展现出调用关系:(图片若无法看清的,可以查看大图或者下载)

     

  • 相关阅读:
    shell script 学习笔记-----标准输出
    《TCP/IP详解 卷一》读书笔记-----TCP persist &Keeplive timer
    《TCP/IP详解 卷一》读书笔记-----TCP超时重传
    《TCP/IP详解 卷一》读书笔记-----TCP数据流
    《TCP/IP详解 卷一》读书笔记-----TCP连接建立
    《TCP/IP详解 卷一》读书笔记-----DNS
    《TCP/IP详解 卷一》读书笔记-----广播&多播&IGMP
    《TCP/IP详解 卷一》读书笔记-----UDP&IP 分片
    《TCP/IP详解 卷一》读书笔记-----动态路由协议
    《TCP/IP 详解 卷一》读书笔记-----IP静态 路由
  • 原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/10591219.html
Copyright © 2011-2022 走看看