springboot 启动分析五

承接启动分析四,本文继续探索springboot的启动之旅

该文主要分析上下文的刷新

public ConfigurableApplicationContext run(String... args) {

    // 声明并实例化一个跑表,用于计算springboot应用程序启动时间
    StopWatch stopWatch = new StopWatch();

    // 启动跑表
    stopWatch.start();

    // 声明一个应用程序上下文,注意这里是一个接口声明
    ConfigurableApplicationContext context = null;

    // 声明一个集合,用于记录springboot异常报告
    Collection exceptionReporters = new ArrayList<>();

    // 配置不在意的属性 java.awt.headless
    configureHeadlessProperty();

    // 获取用于监听spring应用程序run方法的监听器实例
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 循环启动用于run方法的监听器
    listeners.starting();
    try {
        // 封装应用参数
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                args);

        // 根据SpringApplication实例化时候推断出来的应用类型 webApplicationType,
        // 去获取不同的环境,然后获取配置要适用的PropertySource以及激活哪个Profile
        ConfigurableEnvironment environment = prepareEnvironment(listeners,
                applicationArguments);

        // 根据环境配置需要忽略的bean的信息
        configureIgnoreBeanInfo(environment);

        // 根据环境配置打印banner,即根据bannerMode 枚举值,决定是否打印banner和banner打印的位置
        Banner printedBanner = printBanner(environment);

        // key 4 创建应用程序上下文,
        context = createApplicationContext();
        exceptionReporters = getSpringFactoriesInstances(
                SpringBootExceptionReporter.class,
                new Class[]{ConfigurableApplicationContext.class}, context);
        // key 5 准备应用上下文
        prepareContext(context, environment, listeners, applicationArguments,
                printedBanner);
        // key 6 刷新上下文      
        refreshContext(context);
        // key 7 处理刷新上下文之后的事情
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarted(getApplicationLog(), stopWatch);
        }
        listeners.started(context);
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        listeners.running(context);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, exceptionReporters, null);
        throw new IllegalStateException(ex);
    }
    return context;
}

key 6 上下文刷新 refreshContext(context)

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

继续调用了SpringApplication类的受保护的refresh方法

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

调用了上下文实例自己的refresh方法,这里只分析默认的WEB上下文实例,即AnnotationConfigServletWebServerApplicationContext

由于AnnotationConfigServletWebServerApplicationContext 类本身没有重写refresh方法,这里实际上是调用了其抽象父类AbstractApplicationContext的refresh方法

AbstractApplicationContext 抽象应用程序上下文类

是ApplicationContext接口的抽象类,其简单实现了公共上下文功能,使用了模板方法设计模式,需要具体的子类去实现抽象方法

AbstractApplicationContext继承关系如下
springboot 启动分析五_第1张图片
AbstractApplicationContext

ConfigurableApplicationContext接口

可配置应用上下文接口,是一个spi接口,由大多数应用程序上下文实现
提供一个可以配置应用程序上下文的工具,除了只能利用应用程序ApplicationContext接口中的方法

可以理解为这是除了ApplicationContext接口,额外提供了一些可以配置应用上下文的工具接口

刷新上下文方法:

首先看一下在ConfigurableApplicationContext接口中定义的refresh方法的描述

Load or refresh the persistent representation of the configuration, which might an XML file, properties file, or relational database schema.
As this is a startup method, it should destroy already created singletons if it fails, to avoid dangling resources.
In other words, after invocation of that method, either all or no singletons at all should be instantiated.

翻译过来的大概意思就是:
加载或者刷新配置中的持久表示,可能是xml文件,属性文件,或关系数据库定义
由于这是一个启动方法,如果启动失败,应该去销毁所有已创建的单例对象,以避免悬空资源
换句话说,即方法执行之后,要么全部实例化,要么全部不实例化


下面回来看AbstractApplicationContext重写的refresh方法逻辑

该方法定义成了同步,锁对象是一个AbstractApplicationContext 内部定义的私有的用于refresh方法以及destroy方法的同步监视器

/** Synchronization monitor for the "refresh" and "destroy" */
private final Object startupShutdownMonitor = new Object();

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // key 1.1 准备刷新 Prepare this context for refreshing.
        prepareRefresh();

        // key 1.2 告诉子类去刷新内部的bean工厂 Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // key 1.3 准备在此上下文中使用的bean工厂 Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // key 1.4 允许在上下文子类中对bean工厂进行后期处理 Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // key 1.5 执行已经在上下文中注册为bean的工厂处理器 Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // key1.6 注册bean处理器以拦截bean的创建 Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // key1.7 初始化此上下文的消息源 Initialize message source for this context.
            initMessageSource();

            // key1.8 初始化此上下文的事件广播器 Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // key1.9 初始化其他特殊的bean在特殊的上下文子类中 Initialize other special beans in specific context subclasses.
            onRefresh();

            // key 2.0 检查侦听器bean并注册它们 Check for listener beans and register them.
            registerListeners();

            // key2.1 实例化所有非延迟初始化单例 Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // key2.2 最后一步 发布相应的事件 Last step: publish corresponding event.
            finishRefresh();
        }

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

            // 这里注意一下,以上refresh方法的任何一步发生了异常,这里就需要销毁所有已创建的单实例以避免悬空资源 Destroy already created singletons to avoid dangling resources.
            destroyBeans();

            // 重置激活标识 Reset 'active' flag.
            cancelRefresh(ex);

            // 异常上抛给调用者 exception to caller.
            throw ex;
        }

        finally {
            // 重置spring核心的通用缓存 Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            resetCommonCaches();
        }
    }
}

下面根据关键点一一分析这里的各个步骤

key1.1 为刷新上下文做准备

// 为上下文刷新作准备,设置上下文的启动时间和激活标志,以及执行属性源的任何初始化
protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis();
    this.closed.set(false);
    this.active.set(true);
    // key 1 注意一下这里的log日志打印,只有开启INFO级别才会打印,以及打印Refreshing开头拼接上下文实例的toString方法
    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }

    // key2 将该上下文环境中的占位符属性源全部初始化 Initialize any placeholder property sources in the context environment
    initPropertySources();

    // key3 检验所有标记为必须的属性都是可解析的 Validate that all properties marked as required are resolvable
    // see ConfigurablePropertyResolver#setRequiredProperties
    getEnvironment().validateRequiredProperties();

    // key4 定义一个集合,允许收集早期的应用事件,在广播器可用时去发布 Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    this.earlyApplicationEvents = new LinkedHashSet();
}

key 1.1.1 刷新上下文前日志打印信息

打印标志:Refreshing: + 上下文实例的toString方法

// 返回有关上下文的信息
public String toString() {
    StringBuilder sb = new StringBuilder(getDisplayName());
    sb.append(": startup date [").append(new Date(getStartupDate()));
    sb.append("]; ");
    ApplicationContext parent = getParent();
    // 这里获取上下文父上下文,如果为空就会打印"root of context hierarchy" 表示此上下文就是整个上下文结构的根
    if (parent == null) {
        sb.append("root of context hierarchy");
    }
    else {
        sb.append("parent: ").append(parent.getDisplayName());
    }
    return sb.toString();
}

eg:这里举个例子,我们在启动内嵌Web容器的Springboot应用时,会打印

Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@5a9d6f02: startup date [Wed Oct 10 12:52:24 CST 2018]; root of context hierarchy

其中的root of context hierarchy 代表的是 此上下文结构的根就是此上下文实例(AnnotationConfigEmbeddedWebApplicationContext@5a9d6f02),其没有父级上下文


key1.2 obtainFreshBeanFactory() 刷新bean工厂

告诉子类去刷新内部的bean工厂

这里的刷新操作也就是设置了内部bean工厂的id,以及打印了是哪一个bean工厂实例作为该应用上下文的bean工厂

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
        logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
}

这里的refreshBeanFactory方法的实现在GenericApplicationContext 通用应用上下文中定义的刷新内部bean工厂的方法

protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

只是给beanFactory设置唯一id为应用上下文的id即"application"

springboot 启动分析五_第2张图片
DefaultListableBeanFactory

另外注意这里开启debug级别的日志才可以看到类似"Bean factory for XXX"的日志信息

2018-10-11 11:28:48.481 DEBUG 7884 --- [           main] ConfigServletWebServerApplicationContext : Bean factory for org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@74a6f9c1: org.springframework.beans.factory.support.DefaultListableBeanFactory@3212a8d7: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor...]

顺便看一下DefaultListableBeanFactory 类的toString方法

public String toString() {
    StringBuilder sb = new StringBuilder(ObjectUtils.identityToString(this));
    sb.append(": defining beans [");
    sb.append(StringUtils.collectionToCommaDelimitedString(this.beanDefinitionNames));
    sb.append("]; ");
    BeanFactory parent = this.getParentBeanFactory();
    if (parent == null) {
        sb.append("root of factory hierarchy");
    } else {
        sb.append("parent: ").append(ObjectUtils.identityToString(parent));
    }

    return sb.toString();
}

打印了当前bean工厂中已有的bean定义名称集合

key1.3 prepareBeanFactory(beanFactory)

配置Bean工厂

  • 配置bean工厂的classloader(用于加载bean)

  • 配置一些BeanPostProcessor(如ApplicationContextAwareProcessor,ApplicationListenerDetector)

  • 注册一些默认的环境相关的bean(如environment,systemProperties,systemEnvironment)

key1.4 postProcessBeanFactory(beanFactory) beanFactory的后置处理

允许在上下文子类中对bean工厂进行后期处理

postProcessBeanFactory为一个AbstractApplicationContext受保护的方法,不同的子类应用上下文有各自的实现,这里看下AnnotationConfigServletWebServerApplicationContext 上下文实现的逻辑

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    super.postProcessBeanFactory(beanFactory);
    if (this.basePackages != null && this.basePackages.length > 0) {
        this.scanner.scan(this.basePackages);
    }
    if (!this.annotatedClasses.isEmpty()) {
        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }
}

调用父类(ServletWebServerApplicationContext)的postProcessBeanFactory方法

另外注意这里如果basePackages属性有值的话,回去扫描包下的所有bean定义并注册,annotatedClasses属性也是如此吗,具体扫描的细节后续文章会分析

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    beanFactory.addBeanPostProcessor(
            new WebApplicationContextServletContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
}

其实就是向bean工厂中增加了WebApplicationContextServletContextAwareProcessor 处理器

key1.5 invokeBeanFactoryPostProcessors(beanFactory)

执行已经在上下文中注册为bean的工厂处理器

此处会放在后续文章单独分析,这里不做细说

key1.6 registerBeanPostProcessors(beanFactory)

注册bean处理器以拦截bean的创建

这一步主要就是实例化所有已注册的bean后置处理器

这里跟踪看下主要有九个

springboot 启动分析五_第3张图片
postProcessorNames

key1.7 initMessageSource()

初始化此上下文的消息源,即作一些国际化相关属性的初始化

2018-10-11 14:16:14.934 DEBUG 7884 --- [           main] ConfigServletWebServerApplicationContext : Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@6ed06f69]

key1.8 initApplicationEventMulticaster()

初始化此上下文的事件广播器

之前在分析SpringApplication的run方法时用到了应用事件广播器,用于事件的广播

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate ApplicationEventMulticaster with name '" +
                    APPLICATION_EVENT_MULTICASTER_BEAN_NAME +
                    "': using default [" + this.applicationEventMulticaster + "]");
        }
    }
}

这里首先从bean工厂中判断是否有已实例化的applicationEventMulticaster实例,有的化就用这个实例,不在实例化新的

没有的化就new 一个SimpleApplicationEventMulticaster 广播器,并将其注册到bean工厂中,用于后续的事件发布

key1.9 onRefresh()

初始化其他特殊的bean在特殊的上下文子类中

这是一个模板方法,不同的应用上下文作不同的事情,AbstractApplicationContext中的方法是空的,交给各自实现类去实现自己的onRefresh方法

  • ServletWebServerApplicationContext 上下文实现的onRefresh方法 即 AnnotationConfigServletWebServerApplicationContext的父类

    protected void onRefresh() {
    super.onRefresh();
    try {
    createWebServer();
    }
    catch (Throwable ex) {
    throw new ApplicationContextException("Unable to start web server", ex);
    }
    }

主要看createWebServer方法,创建一个内嵌的web服务器

private void createWebServer() {
    WebServer webServer = this.webServer;
    ServletContext servletContext = getServletContext();
    if (webServer == null && servletContext == null) {
        ServletWebServerFactory factory = getWebServerFactory();
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context",
                    ex);
        }
    }
    initPropertySources();
}

目前只支持三种内置的servlet容器

  • Tomcat

  • Jetty

  • Undertow

key2.0 registerListeners()

检查侦听器bean并注册它们

这里将SpringApplication的监听器以及beanFactory的监听器全部都添加到了事件广播器中了,如果存在early event 的化,就直接广播出去

protected void registerListeners() {
    // 注册SpringApplication中的所有监听器 Register statically specified listeners first.
    for (ApplicationListener listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }

    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // 将beanFactory的事件监听器添加进去 uninitialized to let post-processors apply to them!
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }

    // 如果有early event 就直接将事件发布出去 Publish early application events now that we finally have a multicaster...
    Set earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

key2.1 finishBeanFactoryInitialization(beanFactory)

这一步,会将invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这一步中全部实例化,实例化的过程各种BeanPostProcessor就会起作用了

这一步是实例化所有非延迟单例的重点方法
实例化所有非延迟初始化单例

key2.2 finishRefresh()

完成上下文刷新

protected void finishRefresh() {
    // 清空上下文级别的资源缓存 Clear context-level resource caches (such as ASM metadata from scanning).
    clearResourceCaches();

    // key 1 初始化该上下文的生命周期处理器,并设置到spring上下文中 Initialize lifecycle processor for this context.
    initLifecycleProcessor();

    // key 2 传播刷新给生命周期处理器,即调用生命周期处理器的onRefresh方法,该方法会找出容器中实现了SmartLifecycle接口的类并调用其start方法 Propagate refresh to lifecycle processor first.
    getLifecycleProcessor().onRefresh();

    // key 3 发布上下文刷新完成事件 Publish the final event.
    publishEvent(new ContextRefreshedEvent(this));

    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}
key 1 初始化该上下文的生命周期处理器,并设置到spring上下文中

这里会使用DefaultLifecycleProcessor 默认生命周期处理器,如果上下文没有定义过的话

protected void initLifecycleProcessor() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (beanFactory.containsLocalBean(LIFECYCLE_PROCESSOR_BEAN_NAME)) {
        this.lifecycleProcessor =
                beanFactory.getBean(LIFECYCLE_PROCESSOR_BEAN_NAME, LifecycleProcessor.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Using LifecycleProcessor [" + this.lifecycleProcessor + "]");
        }
    }
    else {
        DefaultLifecycleProcessor defaultProcessor = new DefaultLifecycleProcessor();
        defaultProcessor.setBeanFactory(beanFactory);
        this.lifecycleProcessor = defaultProcessor;
        beanFactory.registerSingleton(LIFECYCLE_PROCESSOR_BEAN_NAME, this.lifecycleProcessor);
        if (logger.isDebugEnabled()) {
            logger.debug("Unable to locate LifecycleProcessor with name '" +
                    LIFECYCLE_PROCESSOR_BEAN_NAME +
                    "': using default [" + this.lifecycleProcessor + "]");
        }
    }
}

这里跟踪会进入到else中,因为没有在bean工厂中定义过name为"lifecycleProcessor" 的bean定义

new一个DefaultLifecycleProcessor 默认生命周期处理器,并注册到bean工厂中。

key 2 获取生命周期处理器,并调用器onRefresh方法

这里贴一下DefaultLifecycleProcessor 的onRefresh方法

public void onRefresh() {
    startBeans(true);
    this.running = true;
}

总结

至此,上下文刷新的方法已经简单分析完了,其主要包含了上下文准备,bean工厂的准备,配置类的解析,bean工厂后置处理器处理以及注册,初始化事件广播器,Web内嵌服务器构造,注册监听器,非延迟化bean实例化,最后的完成刷新过程发布相应事件等。

后续会单独一篇文章分析bean定义加载过程,非延迟化bean实例化过程

你可能感兴趣的:(springboot 启动分析五)