Spring源码系列-容器刷新——wac.refresh

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> Spring源码系列-容器刷新——wac.refresh

Spring对于程序员说来说都不陌生;作为一个强大的开源技术,帮助我们能够更好的进行项目的开发与维护。

上次在Spring的启动过程文章中对Spring的启动过程做了一个较为详细的说明和分析。那么在实际的过程中,Spring的启动实际上就是Spring容器的初始化过程。本文将从源码的角度结合自己断点执行过程中保留的现场来分析一下容器的刷新过程(主要分析前几个方法,后面几个会分开来说)。

Spring的启动是通过ContextLoaderListener来进行的,在ContextLoaderListener中通过委托父类ContextLoader的initWebApplicationContext来完成具体的初始化过程。具体的启动过程可以看下之前的那篇文章。

在initWebApplicationContext方法是用来创建容器的,核心代码如下:

Spring源码系列-容器刷新:wac.refresh 今天主要来看configureAndRefreshWebApplicationContext方法中最后的wac.refresh()到底发生了哪些事; Spring源码系列-容器刷新:wac.refresh

1、obtainFreshBeanFactory:BeanFactory的刷新和创建

refresh()方法主要为IoC容器Bean的生命周期管理提供条件,Spring IoC容器载入Bean定义资源文件从其子类容器的refreshBeanFactory()方法启动,所以整个refresh()中“ConfigurableListableBeanFactory beanFactory =obtainFreshBeanFactory();”这句以后代码的都是注册容器的信息源和生命周期事件,载入过程就是从这句代码启动。
AbstractApplicationContext的obtainFreshBeanFactory()方法调用子类容器的refreshBeanFactory()方法,启动容器载入Bean定义资源文件的过程。
refresh()方法的作用是:在创建IoC容器前,如果已经有容器存在,则需要把已有的容器销毁和关闭,以保证在refresh之后使用的是新建立起来的IoC容器。refresh的作用类似于对IoC容器的重启,在新建立好的容器中对容器进行初始化,对Bean定义资源进行载入。
和refreshBeanFactory方法类似,载入Bean定义的方法loadBeanDefinitions也使用了委派模式,在AbstractRefreshableApplicationContext类中只定义了抽象方法,具体的实现调用子类容器中的方法实现。

//通知子类去刷新内部bean 工厂

Spring源码系列-容器刷新:wac.refresh 再来看refreshBeanFactory

此实现执行该上下文的底层bean工厂的实际刷新,关闭以前的bean工厂(如果有的话),并为上下文生命周期的下一阶段初始化一个新的bean工厂。

Spring源码系列-容器刷新:wac.refresh

customizeBeanFactory(DefaultListableBeanFactory beanFactory)


/**
        //通过当前上下文来自定义内部bean工厂br
     * Customize the internal bean factory used by this context.
     * Called for each {@link #refresh()} attempt.
     * pThe default implementation applies this context's
     * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
     * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
     * if specified. Can be overridden in subclasses to customize any of
     * {@link DefaultListableBeanFactory}'s settings.
     * @param beanFactory the newly created bean factory for this context
     * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
     * @see DefaultListableBeanFactory#setAllowCircularReferences
     * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
     * @see DefaultListableBeanFactory#setAllowEagerClassLoading
     */
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
        if (this.allowBeanDefinitionOverriding != null) {
            beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.allowCircularReferences != null) {
            beanFactory.setAllowCircularReferences(this.allowCircularReferences);
        }
    }

XmlWebApplicationContext类中loadBeanDefinitions(beanFactory)


@Override
    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
        // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
        // Configure the bean definition reader with this context's
        // resource loading environment.
        beanDefinitionReader.setEnvironment(getEnvironment());
        beanDefinitionReader.setResourceLoader(this);
        beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
        // Allow a subclass to provide custom initialization of the reader,
        // then proceed with actually loading the bean definitions.
        initBeanDefinitionReader(beanDefinitionReader);
        loadBeanDefinitions(beanDefinitionReader);
    }

AbstractApplicationContext调用loadBeanDefinitions(DefaultListableBeanFactory beanFactory) ,此方法根据首先创建XmlBeanDefinitionReader对象,然后配置该对象的上下文和资源加载环境,同时调用子类实现的initBeanDefinitionReader对XmlBeanDefinitionReader进行个性化配置,最近后入到initBeanDefinitionReader(beanDefinitionReader)的调用:

  • 据给定的BeanFactory创建XmlBeanDefinitionReader 对象  
  • 配置beanDefinitionReader的上下文和资源加载环境  
  • 用子类实现的initBeanDefinitionReader对XmlBeanDefinitionReader进行个性化配置initBeanDefinitionReader(beanDefinitionReader);  
  • 调用载入Bean定义的方法,在当前类中只定义了抽象的loadBeanDefinitions方法,具体的实现调用子类容器  
  • 装载bean定义通过XmlBeanDefinitionReader。

    
    // Create a new XmlBeanDefinitionReader for the given BeanFactory. 通过给定的bean工厂创建一个新的XmlBeanDefinitionReader
    1.XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    2.使用上下文的资源加载环境配置bean定义读取器。
       beanDefinitionReader.setEnvironment(getEnvironment());
       beanDefinitionReader.setResourceLoader(this);
       beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    3.允许子类提供reader的自定义初始化,然后继续实际加载bean定义。
        //通过制定的XmlBeanDefinitionReader来载入beandefinitionReader 
       initBeanDefinitionReader(beanDefinitionReader)
       // 通过制定的XmlBeanDefinitionReader来载入bean definitions
       loadBeanDefinitions(beanDefinitionReader)
    

    AbstractApplicationContext调用loadBeanDefinitions(beanDefinitionReader),这个方法是取得资源或资源路径然后通过传入的reader去加载BeanDefinitions。
    Spring源码系列-容器刷新:wac.refresh

    2、loadBeanDefinitions

    目前使用Spring的配置都是基于XML的,因此使用XmlBeanDefinitionReader 中的loadBeanDefinitions方法。

    Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh 看doLoadBeanDefinitions,这个就是具体的读取文件配置,然后注册成Bean Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh

    3、prepareBeanFactory

    配置工厂的标准上下文特性,如上下文的类装载器和后处理器。

    Spring源码系列-容器刷新:wac.refresh
  • 告诉内部bean工厂使用上下文的类装入器等。
  • 上下文回调配置bean工厂。
  • BeanFactory接口未登记为普通工厂的解析式。MessageSource登记(为自动装配创建)作为一个Bean
  • 如果创建;就去寻找LoadTimeWeaver,然后准备组织
  • 注册默认环境bean。
  • 通过断点来看下当前的beanFactory

    Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh 继续执行… Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh beanDefinitionMap Spring源码系列-容器刷新:wac.refresh manualSingletonNames Spring源码系列-容器刷新:wac.refresh

    4、postProcessBeanFactory

    注册web特性的全局域

    1).registerWebApplicationScopes
    Spring源码系列-容器刷新:wac.refresh
    注册具有web特性的域;包括:”request”, “session”, “globalSession”, “application”

    Spring源码系列-容器刷新:wac.refresh 看下存储结构: Spring源码系列-容器刷新:wac.refresh registerScope方法 Spring源码系列-容器刷新:wac.refresh **2).registerEnvironmentBeans**

    注册web特性 环境bean(“contextparameters”、“ContextAttribute”)与给定的WebApplicationContext使用BeanFactory。

    1.servletContext
    Spring源码系列-容器刷新:wac.refresh
    2.servletConfig
    Spring源码系列-容器刷新:wac.refresh
    3.registerSingleton

    Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh 这里是找到了我们默认的配置文件参数: Spring源码系列-容器刷新:wac.refresh beanName=contextParameters Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh Spring源码系列-容器刷新:wac.refresh 最后是将contextAttributes放入;contextAttributes中包含的属性值比较多,具体如下面所示: Spring源码系列-容器刷新:wac.refresh 主要包括: javax.servlet.context.tempdir, org.apache.catalina.resources, org.springframework.web.context.support.ServletContextScope, org.apache.tomcat.util.scan.MergedWebXml, org.apache.tomcat.InstanceManager, org.apache.catalina.jsp_classpath, javax.websocket.server.ServerContainer, org.apache.tomcat.JarScanner Spring源码系列-容器刷新:wac.refresh 这里是把需要的东西全部载入进来了,有很多。就不贴了(mime-mapping)….

    5、invokeBeanFactoryPostProcessors

    BeanDefinitionRegistryPostProcessor实例化:标准BeanFactoryPostProcessor的扩展,BeanFactoryPostProcessor的作用是用来进一步定义注册的BeanDefinition,IoC容器本质就是Bean管理,所以BeanFactoryPostProcessor本身也是Bean,要对BeanFactoryPostProcessor的BeanDefinition进一步定义就通过BeanDefinitionRegistryPostProcessor进行注册,BeanDefinitionRegistryPostProcessor及其子类是Ioc容器最实例化的一类Bean。它们在ConfigurableApplicationContext(ApplicationContext子接口)实现类调用refresh()方法调用invokeBeanFactoryPostProcessors(beanFactory);方法时就被实例化。

    OK,今天关于这部分的分析就到此结束了,后面的过程会在下一篇Spring系列文章中继续来讲refresh中的过程。

    如果您对系列文章有任何意见,可以给我留言,感谢大家。

    原文始发于微信公众号(glmapper工作室):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> Spring源码系列-容器刷新——wac.refresh


     上一篇
    Spring源码系列——BeanFactory的创建 Spring源码系列——BeanFactory的创建
    Spring的Ioc容器其实就是一个bean的关系网,依赖于core,bean,context三个组件来构建的。在spring中最核心的就是对于bean的管理。而bean又依托于我们的容器。本文将从顶层分析一下spring中beanFact
    2021-04-05
    下一篇 
    Spring源码系列——依赖注入(一)(AbstractBeanFactory-getBean) Spring源码系列——依赖注入(一)(AbstractBeanFactory-getBean)
    在Spring源码系列:BeanFactory的创建文章中我们谈到了BeanFactory这容器,这个里面提供了注入的实现接口。其具体的实现还需要从AbstractBeanFactory和DefaultListableBeanFactory
    2021-04-05