Spring源码阅读笔记(二):ApplicationContext初始化简要流程

(如果对于Spring的整体架构和核心技术不了解,建议先看此篇–Spring源码阅读笔记(一):整体架构与核心技术)

基于Spring框架的应用,最主要的亮点就是bean的IoC。而IoC容器的配置、组装由ApplicationContext完成,那么我们就从这里开始,去探究IoC如何管理到bean对象。

ApplicationContext初始化简要流程

Spring的应用首先需要定义一个ApplicationContext,此处我们定义的ApplicationContextClassPathXmlApplicationContext,它的UML图如下:

Spring源码阅读笔记(二):ApplicationContext初始化简要流程_第1张图片

我们以XML文件配置Spring来进行分析,首先我们的启动类是这样:

public class Application{
  public static void main(String[] args) {
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/context.xml");
    ctx.start();
  }
}

当我们new ClassPathXmlApplicationContext("/context.xml")的时候,执行ClassPathXmlApplicationContext构造器,类内部调用:

  public ClassPathXmlApplicationContext(String... configLocations) throws BeansException {
        this(configLocations, true, null);
    }

    public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {
        super(parent);
        setConfigLocations(configLocations);
        if (refresh) {
            refresh();
        }
    }

此处调用super(parent)主要的目的是根据ClassPathXmlApplicationContext的继承关系构建其上下文环境。 按照继承的顺序依次是DefaultResourceLoader <– AbstractApplicationContext <– AbstractRefreshableApplicationContext <– AbstractRefreshableConfigApplicationContext <– AbstractXmlApplicationContext

接下来我们依次分析每个类的作用:

  1. DefaultResourceLoader: 继承此类的目的主要是为了获取类加载器classLoader以及默认实现根据字符串configLocations加载具体的Resource(在ClassPathXml)。 作为ResourceLoader的默认实现,它不仅提供了Resource getResource(String location)的实现,还额外设置了默认的classlLoader–this.classLoader = ClassUtils.getDefaultClassLoader();.

    • classLoader: 类加载器的的获取顺序依次为Thread.currentThread().getContextClassLoader() –> ClassUtils.class.getClassLoader(); –> cl = ClassLoader.getSystemClassLoader();
    • Resource getResource(String location): 此方法的目的要将location封装为Resource. 针对不同格式的location,采用不同的策略(以下也为解析的顺序):
      • locationftp://, http://, file://等协议开头:通过ProtocolResolver对象进行解析,在类内部,持有了ProtocolResolver的集合,可以将不同的协议的解析方式以插件的形式插入.
      • location/开头: 通过静态内部类ClassPathContextResource包装为Resource,此类主要使用类或者类加载器的getResourceAsStream(this.path)方法加载.
      • locationclasspath:开头: 与/开头类似,也是通过静态内部类ClassPathContextResource包装为Resource, 使用类或者类加载器的getResourceAsStream(this.path)方法加载
      • 其他形式: 首先将其当做一个URL,所以先通过URL url = new URL(location);return new UrlResource(url)封装为UrlResource,如果出错,则处理方式与上同.
  2. AbstractApplicationContext: 此类的构造器中首先设置了ResourcePatternResolverPathMatchingResourcePatternResolver,这个类的目的是根据输入的configLocations,寻找对应目录下的资源(包括war://).

  3. AbstractRefreshableApplicationContext: 主要定义了重新刷新BeanFactory的方法refreshBeanFactory(),包括设置是否可重写BeanDefinitions、以及是否允许循环应用;
  4. AbstractRefreshableConfigApplicationContext: 主要讲加载的文件路径中的表达式替换为对应的值,方法为resolvePath(String path)
  5. AbstractXmlApplicationContext: 主要定义了用于加载BeanDefinitions的默认实现,相关方法loadBeanDefinitions(DefaultListableBeanFactory beanFactory)

当以上的环境准备好之后,接下来就要进行整个加载流程的核心方法refresh()

@Override
public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            prepareRefresh();

            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            prepareBeanFactory(beanFactory);

            try {
                postProcessBeanFactory(beanFactory);

                invokeBeanFactoryPostProcessors(beanFactory);

                registerBeanPostProcessors(beanFactory);

                initMessageSource();

                initApplicationEventMulticaster();

                onRefresh();

                registerListeners();

                finishBeanFactoryInitialization(beanFactory);

                finishRefresh();
            }   catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +  "cancelling refresh attempt: " + ex);
                }

                // 摧毁已创建的bean避免资源浪费
                destroyBeans();

                // 重置`active`标识
                cancelRefresh(ex);

                // 传播异常至调用者
                throw ex;
            }   finally {
                resetCommonCaches();
            }
        }
    }

通过refresh()内部的实现我们大致可以了解整个refresh()方法担负了整个Spring容器初始化和加载的所有逻辑,包括Bean工厂的初始化、post-processor的注册以及调用、bean的实例化、事件发布等。

下面,我们简要的分析此方法内流程逻辑:

  1. prepareRefresh(), 为刷新准备上下文,主要设置状态量(是否关闭、是否激活)、记录启动时间、初始化属性资源占位符、校验必填属性是否配置以及初始化用于存储早期应用事件的容器.
  2. obtainFreshBeanFactory(),主要用于获取一个新的BeanFactory,如果BeanFactory已存在,则将其销毁并重建,默认重建的BeanFactory为AbstractRefreshableApplicationContext;此外,此方法委托其子类从XML中或者基于注解的类中加载BeanDefinition,详细过程请关注后续博客.
  3. prepareBeanFactory() 配置BeanFatory使其具有一个上下文的的标准特征,如上下文的类加载器、后处理程序(post-processors,如设置各种感知接口等)
  4. postProcessBeanFactory() 在应用上下文内部的BeanFactory初始化结束后对其进行修改,载所有的BeanDefinition已被加载但还没有实例化bean, 此刻可以注册一些特殊的BeanPostProcessors,如web应用会注册一些ServletContextAwareProcessor
  5. invokeBeanFactoryPostProcessors() 调用注册在上下文中的BeanFactoryPostProcessor,如果给与顺序则按照属性调用,并且一定在单例对象实例化之前调用
  6. registerBeanPostProcessors() 实例化并调用所有注册的BeanPostProcessor,如果有显式的顺序则按照顺序调用;一定在任意的bean实例化之前调用
  7. initMessageSource()初始化MessageSource,如果当前上下文没有定义则使用其父类的. 如果BeanFactory中不能找到名称为messageSource的bean, 则默认使用DelegatingMessageSource
  8. initApplicationEventMulticaster()初始化ApplicationEventMulticaster,如果上下文没有定义则默认使用SimpleApplicationEventMulticaster,此类主要用于广播ApplicationEvent
  9. onRefresh()在一些特定的上下文子类中初始化特定的bean, 如在Webapp的上下文中初始化主题资源;
  10. registerListeners() 添加实现了ApplicationListener的bean作为监听器,它不影响非bean的监听器;它还会使用多播器发布早期的ApplicationEvent
  11. finishBeanFactoryInitialization() 实例化所有非延迟加载的单例,完成bean factory的初始化工作;
  12. finishRefresh() 完成上下文的刷新工作,调用LifecycleProcessoronFresh()方法以及发布ContextRefreshedEvent事件
  13. resetCommonCaches() 重置Spring公共的缓存,比较典型的如: ReflectionUtils, ResolvableType以及CachedIntrospectionResults的缓存

至此,整个应用上下文建立成功.

你可能感兴趣的:(Spring)