[Spring boot源码解析] 2 启动流程分析

入口类

在了解 Spring Boot 的启动流程的时候,我们先看一下一个Spring Boot 应用是如何启动的,如下是一个简单的 SpringBoot 程序,非常的简洁,他是如何做到的呢,我们接下来就将一步步分解。

@SpringBootApplication
public class SampleKafkaApplication {

    public static void main(String[] args) {
        SpringApplication.run(SampleKafkaApplication.class, args);
    }
}

我们追踪 SpringApplication.run() 方法,其实最终它主要的逻辑是新建一个 SpringApplication,然后调用他的 run 方法,如下:

    public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
        return new SpringApplication(primarySources).run(args);
    }

我们先来看一下创建 SpringApplication 的方法:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.resourceLoader = resourceLoader;
        Assert.notNull(primarySources, "PrimarySources must not be null");
        // 将 Main Class 设置为自己的元素
        this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
        // 检查当先的 app 类型
        this.webApplicationType = WebApplicationType.deduceFromClasspath();
        // 先从 Spring.factories 文件中加载 ApplicationContextInitializer 类信息。
        setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
        // 先从 Spring.factories 文件中加载 ApplicationListener 类信息。
        setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
        // 获取 main class 信息,并设置到本地属性中
        this.mainApplicationClass = deduceMainApplicationClass();
    }

检查应用类型

在将Main class 设置primarySources 后,调用了 WebApplicationType.deduceFromClasspath() 方法,该方法是为了检查当前的应用类型,并设置给 webApplicationType。 我们进入 deduceFromClasspath 方法 :

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
                && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
            return WebApplicationType.REACTIVE;
        }
        for (String className : SERVLET_INDICATOR_CLASSES) {
            if (!ClassUtils.isPresent(className, null)) {
                return WebApplicationType.NONE;
            }
        }
        return WebApplicationType.SERVLET;
    }

这里主要是通过类加载器判断是否存在 REACTIVE 相关的类信息,假如有就代表是一个 REACTIVE 的应用,假如不是就检查是否存在 ServeltConfigurableWebApplicationContext ,假如都没有,就代表应用为非 WEB 类应用,返回 NONE,默认返回 SERVLET 类型,我们这期以我们目前最常使用的 SERVLET 类型进行讲解,所以我们在应用中引入了 spring-boot-starter-web 作为依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

他会包含 Spring-mvc 的依赖,所以就包含了内嵌 tomcat 中的 Servlet 和 Spring-web 中的 ConfigurableWebApplicationContext,因此返回了 SERVLET 类型。

设置初始化器 Initializer

回到刚才创建 SpringApplication 的构建方法中,我们设置完成应用类型后,就寻找所有的 Initializer 实现类,并设置到SpringApplicationInitializers 中,这里先说一下 getSpringFactoriesInstances 方法,我们知道在我们使用 SpringBoot 程序中,会经常在 META-INF/spring.factories 目录下看到一些EnableAutoConfiguration,来出发 config 类注入到容器中,我们知道一般一个 config 类要想被 SpringBoot 扫描到需要使用 @CompnentScan 来扫描具体的路径,对于 jar 包来说这无疑是非常不方便的,所以 SpringBoot 提供了另外一种方式来实现,就是使用 spring.factories ,比如下面这个,我们从 Springboot-test 中找到的例子,这里先定义了一个ExampleAutoConfiguration,并加上了Configuration 注解:

@Configuration
public class ExampleAutoConfiguration {
    @Bean
    @ConditionalOnWarDeployment
    public ServletRegistrationBean<TestServlet> onWarTestServlet() {
        ServletRegistrationBean<TestServlet> registration = new ServletRegistrationBean<>(new TestServlet());
        registration.addUrlMappings("/conditionalOnWar");
        return registration;
    }
    @Bean
    public ServletRegistrationBean<TestServlet> testServlet() {
        ServletRegistrationBean<TestServlet> registration = new ServletRegistrationBean<>(new TestServlet());
        registration.addUrlMappings("/always");
        return registration;
    }
}

然后在 spring.factories 中定义如下:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.autoconfig.ExampleAutoConfiguration

那这种方式是怎么实现的你,这就要回到我们刚才的方法getSpringFactoriesInstances :

    private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = getClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

我们先来看一下传入参数,这里需要注意的是 args,这个是初始化对应 type 的时候传入的构造参数,我们先看一下 SpringFactoriesLoader#loadFactoryNames 方法:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                UrlResource resource = new UrlResource(url);
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
}

首先是会先检查缓存,假如缓存中存在就直接返回,假如没有就调用 classLoader#getResources 方法,传入META-INF/spring.factories,即获取所有 jar 包下的对应文件,并封装成 UrlResource ,然后使用 PropertiesLoaderUtils 将这些信息读取成一个对一对的 properties,我们观察一下 spring.factories 都是按 properties 格式排版的,假如有多个就用逗号隔开,所以这里还需要将逗号的多个类分隔开来,并加到 result 中,由于 result 是一个 LinkedMultiValueMap 类型,支持多个值插入,最后放回缓存中。最终完成加载 META-INF/spring.factories 中的配置,如下:

截屏2021-11-29 上午8.03.37.png

我们可以看一下我们找到的 initializer 有多少个:

initializer

除了默认的 7 个 initializer 外,这里还引入了 Apollo ,所以还有一个 ApolloApplicationcontextInitializer , 我们来看一下 Initializer 接口做了那些东西,其实主要是实现了 initialize 方法:

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}

在获取到所有的 Initializer 后接下来是调用 createSpringFactoriesInstances 方法进行初始化。

private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
            ClassLoader classLoader, Object[] args, Set<String> names) {
        List<T> instances = new ArrayList<>(names.size());
        // 这里包括很多初始化类信息,包括 apollo , shardingShepre 都是在这里初始化。
        for (String name : names) {
            try {
                Class<?> instanceClass = ClassUtils.forName(name, classLoader);
                Assert.isAssignable(type, instanceClass);
                Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
                T instance = (T) BeanUtils.instantiateClass(constructor, args);
                instances.add(instance);
            }
            catch (Throwable ex) {
                throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
            }
        }
        return instances;
    }

这里的 names 就是我们上面通过类加载器加载到的类名,到这里会先通过反射生成 class 对象,然后判断该类是否继承与 ApplicationContextInitializer ,最后通过发射的方式获取这个类的构造方法,并调用该构造方法,传入已经定义好的构造参数,对于 ApplicationContextInitializer 是无参的构造方法,然后初始化实例并返回,回到原来的方法,这里会先对所有的 ApplicationContextInitializer 进行排序,调用 AnnotationAwareOrderComparator#sort(instances) 方法,这里就是根据 @Order 中的顺序进行排序。

设置监听器

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

接下来是设置 ApplicationListener,我们跟进去就会发现这里和上面获取 ApplicationContextInitializer 的方法如出一辙,最终会加载到如图的 15 个 listener (这里除了 EnableEncryptablePropertiesBeanFactoryPostProcessor 外,其他都是 SpringBoot 内部的 Listener):

listener

SpringApplication#run 方法

在完成 SpringApplication 对象的初始化后,我们进入了他的 run 方法,这个方法几乎涵盖了 SpringBoot 生命周期的所有内容,主要分为九个步骤,每一个步骤这里都使用注解进行标识:

    public ConfigurableApplicationContext run(String... args) {
        // 启动计时器计算初始化完成耗时
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        configureHeadlessProperty();
        // 第一步:获取 SpringApplicationRunListener, 然后调用他的 staring 方法启动监听器。
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
            // 第二步:根据 SpringApplicationRunListeners以及参数来准备环境
            ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
            configureIgnoreBeanInfo(environment);
            // 准备打印 Banner
            Banner printedBanner = printBanner(environment);
            // 第三步:创建 Spring 容器
            context = createApplicationContext();
            // 第四步: Spring 容器的前置处理
            prepareContext(context, environment, listeners, applicationArguments, printedBanner);
            // 第五步:刷新 Spring 容器
            refreshContext(context);
            // 第六步: Spring 容器的后置处理器
            afterRefresh(context, applicationArguments);
            // 停止计时
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
            }
            //第七步:通知所有 listener 结束启动
            listeners.started(context);
            //第八步:调用所有 runner 的 run 方法
            callRunners(context, applicationArguments);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, listeners);
            throw new IllegalStateException(ex);
        }
        //第九步:通知所有 listener running 事件
        try {
            listeners.running(context);
        }
        catch (Throwable ex) {
            handleRunFailure(context, ex, null);
            throw new IllegalStateException(ex);
        }
        return context;
    }

主要步骤如下:
第一步:获取 SpringApplicationRunListener, 然后调用他的 staring 方法启动监听器。
第二步:根据 SpringApplicationRunListeners以及参数来准备环境。
第三步:创建 Spring 容器。
第四步:Spring 容器的前置处理。
第五步:刷新 Spring 容器。
第六步: Spring 容器的后置处理器。
第七步:通知所有 listener 结束启动。
第八步:调用所有 runner 的 run 方法。
第九步:通知所有 listener running 事件。
我们接下来一一讲解这些内容。

1. 获取 SpringApplicationRunListener

我们首先看一下第一步,获取 SpringApplicationRunListener:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger,
                getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
    }

这里和上面获取 initializerlistener 的方式基本一致,都是通过 getSpringFactoriesInstances, 最终只找到一个类就是:org.springframework.boot.context.event.EventPublishingRunListener ,然后调用其构造方法并传入产生 args , 和 SpringApplication 本身:

public class EventPublishingRunListener implements SpringApplicationRunListener, Ordered {

    private final SpringApplication application;

    private final String[] args;

    private final SimpleApplicationEventMulticaster initialMulticaster;

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }
}

我们先看一下构造函数,首先将我们获取到的ApplicationListener 集合添加到initialMulticaster 中, 最后都是通过操作 SimpleApplicationEventMulticaster 来进行广播,我,他继承于 AbstractApplicationEventMulticaster,我们先看一下他的 addApplicationListener 方法:

@Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        synchronized (this.defaultRetriever) {
            Object singletonTarget = AopProxyUtils.getSingletonTarget(listener);
            if (singletonTarget instanceof ApplicationListener) {
                this.defaultRetriever.applicationListeners.remove(singletonTarget);
            }
            this.defaultRetriever.applicationListeners.add(listener);
            this.retrieverCache.clear();
        }
    }

我们可以看出,最后是放到了applicationListenters 这个容器中。他是 defaultRetriever 的成员属性, defaultRetriever 则是AbstractApplicationEventMulticaster 的私有类,我们简单看一下这个类:

private class DefaultListenerRetriever {

        public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet<>();

        public final Set<String> applicationListenerBeans = new LinkedHashSet<>();

        public Collection<ApplicationListener<?>> getApplicationListeners() {
            List<ApplicationListener<?>> allListeners = new ArrayList<>(
                    this.applicationListeners.size() + this.applicationListenerBeans.size());
            allListeners.addAll(this.applicationListeners);
            if (!this.applicationListenerBeans.isEmpty()) {
                BeanFactory beanFactory = getBeanFactory();
                for (String listenerBeanName : this.applicationListenerBeans) {
                    try {
                        ApplicationListener<?> listener =
                                beanFactory.getBean(listenerBeanName, ApplicationListener.class);
                        if (!allListeners.contains(listener)) {
                            allListeners.add(listener);
                        }
                    }
                    catch (NoSuchBeanDefinitionException ex) {
                        // Singleton listener instance (without backing bean definition) disappeared -
                        // probably in the middle of the destruction phase
                    }
                }
            }
            AnnotationAwareOrderComparator.sort(allListeners);
            return allListeners;
        }
    }

我们只需要看一下这里的 getApplicationListeners 方法,它主要是到 beanFactory 中检查是否存在多的 ApplicationListener和旧的 applicationListeners组合并返回,接着执行 listenerstart 方法,最后也是调用了 AbstractApplicationEventMulticastermulticastEvent 查找支持对应的 ApplicationEvent 类型的通知的 ApplicationListeneronApplicationEvent 方法 ,这里除了会:

@Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            }
            else {
                invokeListener(listener, event);
            }
        }
    }

筛选的方法如下,都是调用了对应类型的supportsEventType 方法 :

    protected boolean supportsEvent(
            ApplicationListener<?> listener, ResolvableType eventType, @Nullable Class<?> sourceType) {

        GenericApplicationListener smartListener = (listener instanceof GenericApplicationListener ?
                (GenericApplicationListener) listener : new GenericApplicationListenerAdapter(listener));
        return (smartListener.supportsEventType(eventType) && smartListener.supportsSourceType(sourceType));
    }

如图,我们可以看到对org.springframework.boot.context.event.ApplicationStartingEvent 感兴趣的有5个 Listener

Listener

2.环境准备

环境准备的具体方法如下:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments) {
        // 根据类型创建对应的 environment
        ConfigurableEnvironment environment = getOrCreateEnvironment();
        // 配置 environment 信息
        configureEnvironment(environment, applicationArguments.getSourceArgs());
        ConfigurationPropertySources.attach(environment);
        // 发送 prepareEnviroment 事件
        listeners.environmentPrepared(environment);
        bindToSpringApplication(environment);
        if (!this.isCustomEnvironment) {
            environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                    deduceEnvironmentClass());
        }
        ConfigurationPropertySources.attach(environment);
        return environment;
    }

首先是调用 getOrCreateEnvironment 方法来创建environment,我们跟进去可以发现这里是根据我们上面设置的环境的类型来进行选择的,当前环境会创建 StandardServletEnvironment

private ConfigurableEnvironment getOrCreateEnvironment() {
        if (this.environment != null) {
            return this.environment;
        }
        switch (this.webApplicationType) {
        case SERVLET:
            return new StandardServletEnvironment();
        case REACTIVE:
            return new StandardReactiveWebEnvironment();
        default:
            return new StandardEnvironment();
        }
    }

我们先来看一下 StandardServletEnvironment 的类继承关系图,我们可以看出他是继承了 AbstractEnvironment

StandardServletEnvironment

当我们调用StandardServletEnvironment 无参的构造方法的时候,最终调用的是 AbstractEnvironment 的构造方法:

public AbstractEnvironment() {
        customizePropertySources(this.propertySources);
        if (this.logger.isDebugEnabled()) {
            this.logger.debug(format(
                    "Initialized %s with PropertySources %s", getClass().getSimpleName(), this.propertySources));
        }
    }

他会调用子类的 customizePropertySources 方法实现,首先是 StandardServletEnvironment 的实现如下,他会添加 servletConfigInitParams, servletContextInitParams, jndiProperties 三种 properties,当前调试环境没有配置 jndi properties,所以这里不会添加。接着调用父类的 customizePropertySources方法,即调用到了 StandardEnvironment

    @Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(new StubPropertySource(SERVLET_CONFIG_PROPERTY_SOURCE_NAME));
        propertySources.addLast(new StubPropertySource(SERVLET_CONTEXT_PROPERTY_SOURCE_NAME));
        if (JndiLocatorDelegate.isDefaultJndiEnvironmentAvailable()) {
            propertySources.addLast(new JndiPropertySource(JNDI_PROPERTY_SOURCE_NAME));
        }
        super.customizePropertySources(propertySources);
    }

我们看一下 StandardEnvironment#customizePropertySources 方法,与上面的三个 properties 创建不同,这两个是会进行赋值的,包括系统环境变量放入 systemEnvironment 中,jvm 先关参数放到 systemProperties 中:

@Override
    protected void customizePropertySources(MutablePropertySources propertySources) {
        propertySources.addLast(
                new PropertiesPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
        propertySources.addLast(
                new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
    }

这里会添加 systemEnvironmentsystemProperties 这两个 properties,最终拿到的 properties 数量如下 4个:

image.png

配置环境信息

在创建完成 Environment 后,接下来就到了调用 configureEnvironment 方法:

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService) conversionService);
        }
        // 配置PropertySources
        configurePropertySources(environment, args);
        // 配置Profiles
        configureProfiles(environment, args);
    }

我们先看一下 configurePropertySources 方法,这里主要分两部分,首先是查询当前是否存在 defaultProperties ,假如不为空就会添加到 environmentpropertySources 中,接着是处理命令行参数,将命令行参数作为一个 CompositePropertySource 或则 SimpleCommandLinePropertySource 添加到 environmentpropertySources 里面,

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        if (this.defaultProperties != null && !this.defaultProperties.isEmpty()) {
            sources.addLast(new MapPropertySource("defaultProperties", this.defaultProperties));
        }
        if (this.addCommandLineProperties && args.length > 0) {
            String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(
                        new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            }
            else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }

接着调用 ConfigurationPropertySources#attach 方法,他会先去 environment 中查找 configurationProperties , 假如寻找到了,先检查 configurationProperties 和当前 environment 是否匹配,假如不相等,就先去除,最后添加 configurationProperties 并将其 sources 属性设置进去。

public static void attach(Environment environment) {
        Assert.isInstanceOf(ConfigurableEnvironment.class, environment);
        MutablePropertySources sources = ((ConfigurableEnvironment) environment).getPropertySources();
        PropertySource<?> attached = sources.get(ATTACHED_PROPERTY_SOURCE_NAME);
        if (attached != null && attached.getSource() != sources) {
            sources.remove(ATTACHED_PROPERTY_SOURCE_NAME);
            attached = null;
        }
        if (attached == null) {
            sources.addFirst(new ConfigurationPropertySourcesPropertySource(ATTACHED_PROPERTY_SOURCE_NAME,
                    new SpringConfigurationPropertySources(sources)));
        }
    }

发送通知

ConfigFileApplicationListener

回到我们的 prepareEnvironment 逻辑,下一步是通知观察者,发送 ApplicationEnvironmentPreparedEvent 事件,调用的是 SpringApplicationRunListeners#environmentPrepared 方法,最终回到了 SimpleApplicationEventMulticaster#multicastEvent 方法,我们通过 debug 找到最后对这个时间感兴趣的 Listener 如下:

Listener

这里我们需要着重看一下 ConfigFileApplicationListener , 它主要负责加载 application.propertiesapplication.yml 从如下路径:./config/;./;classpath:config/;classpath:。我们来看一下接收通知的代码,最终调用到的是 onApplicationEnvironmentPreparedEvent

private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
        List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
        postProcessors.add(this);
        AnnotationAwareOrderComparator.sort(postProcessors);
        for (EnvironmentPostProcessor postProcessor : postProcessors) {
            postProcessor.postProcessEnvironment(event.getEnvironment(), event.getSpringApplication());
        }
    }

其主要逻辑如下:

  1. 调用 loadPostProcessors 方法,加载所有的 EnvironmentPostProcessor
  2. 将加载到的 EnvironmentPostProcessor 存储到本地。
  3. 根据各个 EnvironmentPostProcessor 的 @order 属性进行排序。
  4. 调用各个 EnvironmentPostProcessorpostProcessEnvironment 方法。
    我们来看一下具体有哪些 EnvironmentPostProcessor, 如图我们有 8 个 Spring Boot 自带的 EnvironmentPostProcessor
    EnvironmentPostProcessor

    我们可以看到 ConfigFileApplicationListener 自身也是一个 EnvironmentPostProcessor , 我们看一下他的 postProcessEnvironment 方法:
    其主要实现如下:
protected void addPropertySources(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
        RandomValuePropertySource.addToEnvironment(environment);
        new Loader(environment, resourceLoader).load();
    }
  1. 向 Environment 添加 randomPropertySource
  2. 创建一个新的 Loader 对象,然后调用他的 load 方法。
    我们先看一下创建新的 Loader 对象,这里创建的 PropertySourcesPlaceholdersResolver 对象主要作用是提供 resolvePlaceholders 方法,遍历所有的 PropertySource 然后查找对应的 property。。DefaultResourceLoader 则主要用于加载资源:
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
           this.environment = environment;
           this.placeholdersResolver = new PropertySourcesPlaceholdersResolver(this.environment);
           this.resourceLoader = (resourceLoader != null) ? resourceLoader : new DefaultResourceLoader(null);
           this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
                   getClass().getClassLoader());
       }

这个方法最后加载了PropertySourceLoader , 这里主要是两种,一个是用于 Properties 的,一个是用于 YAML 的如下:

PropertySourceLoader

下一步是调用其 load 方法,加载资源项:

void load() {
            FilteredPropertySource.apply(this.environment, DEFAULT_PROPERTIES, LOAD_FILTERED_PROPERTY,
                    (defaultProperties) -> {
                        this.profiles = new LinkedList<>();
                        this.processedProfiles = new LinkedList<>();
                        this.activatedProfiles = false;
                        this.loaded = new LinkedHashMap<>();
                        initializeProfiles();
                        while (!this.profiles.isEmpty()) {
                            Profile profile = this.profiles.poll();
                            if (isDefaultProfile(profile)) {
                                addProfileToEnvironment(profile.getName());
                            }
                            load(profile, this::getPositiveProfileFilter,
                                    addToLoaded(MutablePropertySources::addLast, false));
                            this.processedProfiles.add(profile);
                        }
                        load(null, this::getNegativeProfileFilter, addToLoaded(MutablePropertySources::addFirst, true));
                        addLoadedPropertySources();
                        applyActiveProfiles(defaultProperties);
                    });
        }

其中 apply 方法主要是加载 defaultProperties ,假如已经存在,就进行替换,而替换的目标 PropertySource 就是 load这里最后的一个 consumer 函数加载出来的,这里列一下主要做的事情:
1、加载系统中设置的所有的 Profile
2、遍历所有的 Profile,假如是默认的 Profile, 就将这个 Profile 加到 environment 中。
3、调用load 方法,加载配置,我们深入看一下这个方法:

private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
            getSearchLocations().forEach((location) -> {
                boolean isDirectory = location.endsWith("/");
                Set<String> names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
                names.forEach((name) -> load(location, name, profile, filterFactory, consumer));
            });
        }

他会先调用 getSearchLocations 方法,加载所有的需要加载的路径,最终有如下路径:

locations

然后调用 getSearchNames 方法查询需要查询的名称,这里没有定义,默认就是application 。接着调用真的的 load 方法,如下:

private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
                DocumentConsumer consumer) {
            if (!StringUtils.hasText(name)) {
                for (PropertySourceLoader loader : this.propertySourceLoaders) {
                    if (canLoadFileExtension(loader, location)) {
                        load(loader, location, profile, filterFactory.getDocumentFilter(profile), consumer);
                        return;
                    }
                }
                throw new IllegalStateException("File extension of config file location '" + location
                        + "' is not known to any PropertySourceLoader. If the location is meant to reference "
                        + "a directory, it must end in '/'");
            }
            Set<String> processed = new HashSet<>();
            for (PropertySourceLoader loader : this.propertySourceLoaders) {
                for (String fileExtension : loader.getFileExtensions()) {
                    if (processed.add(fileExtension)) {
                        loadForFileExtension(loader, location + name, "." + fileExtension, profile, filterFactory,
                                consumer);
                    }
                }
            }
        }

其核心方法是遍历所有的 propertySourceLoader,也就是上面加载到两种propertySourceLoader,最红 loadForFileExtension 方法,加载配置文件,这里就不展开分析了,说一下主要的作用,因为每个 propertySourceLoader 都有自己可以加载的扩展名,默认扩展名有如下四个 properties, xml, yml, yaml,所以最终拿到文件名字,然后通过 - 拼接所有的真实的名字,然后加上路径一起加载。

BackgroundPreinitializer

接下来,我们分析 BackgroundPreinitializer,这个方法在接收 ApplicationPrepareEnvironment 事件的时候真正调用了这份方法:

private void performPreinitialization() {
        try {
            Thread thread = new Thread(new Runnable() {

                @Override
                public void run() {
                    runSafely(new ConversionServiceInitializer());
                    runSafely(new ValidationInitializer());
                    runSafely(new MessageConverterInitializer());
                    runSafely(new JacksonInitializer());
                    runSafely(new CharsetInitializer());
                    preinitializationComplete.countDown();
                }

                public void runSafely(Runnable runnable) {
                    try {
                        runnable.run();
                    }
                    catch (Throwable ex) {
                        // Ignore
                    }
                }

            }, "background-preinit");
            thread.start();
        }
        catch (Exception ex) {
            preinitializationComplete.countDown();
        }
    }

1、ConversionServiceInitializer 主要负责将包括 日期,货币等一些默认的转换器注册到 formatterRegistry 中。
2、ValidationInitializer 创建 validation 的匹配器。
3、MessageConverterInitializer 主要是添加了一些 http 的 Message Converter。
4、JacksonInitializer 主要用于生成 xml 转换器的。
接着回到我们将的主体方法,prepareEnvironment 在调用完成 listeners.environmentPrepared(environment) 方法后,调用 bindToSpringApplication(environment) 方法,将 environment 绑定到 SpirngApplication 中。
接着将 enviroment 转化为 StandardEnvironment 对象。
最后将 configurationProperties 加入到 enviroment 中, configurationProperties 其实是将 environment 中其他的 PropertySource 重新包装了一遍,并放到 environment 中,这里主要的作用是方便 PropertySourcesPropertyResolver 进行解析。

configureIgnoreBeanInfo

它主要是检查是否存在 spring.beaninfo.ignore 配置,这个配置的主要作用是设置 javaBean 的内省模式,所谓内省就是应用程序在 Runtime 的时候能检查对象类型的能力,通常也可以称作运行时类型检查,区别于反射主要用于修改类属性,内省主要用户获取类属性。那么我们什么时候会使用到内省呢,java主要是通过内省工具 Introspector 来完成内省的工作,内省的结果通过一个 Beaninfo 对象返回,主要包括类的一些相关信息,而在 Spring中,主要是 BeanUtils#copyProperties会使用到,Spring 对内省机制还进行了改进,有三种内省模式,如下图中红色框框的内容,默认情况下是使用 USE_ALL_BEANINFO。假如设置为true,就是改成第三中 IGNORE_ALL_BEANINFO

Spring 内省

3.创建 ApplicationContext

首先是检查 Application的类型,然后获取对应的 ApplicationContext 类,我们这里是获取到了 org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext 接着调用 BeanUtils.instantiateClass(contextClass); 方法进行对象的初始化。

protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch (this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                    break;
                case REACTIVE:
                    contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                    break;
                default:
                    contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }

最终其实是调用了 AnnotationConfigServletWebServerApplicationContext 的默认构造方法。我们看一下这个方法做了什么事情。这里只是简单的设置了一个 reader 和一个 scanner,作用于 bean 的扫描工作。

    public AnnotationConfigServletWebServerApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

我们再来看一下这个类的继承关系

AnnotationConfigServletWebServerApplicationContext.png

我们可以看出来AnnotationConfigServletWebServerApplicationContext 的父类为 ServletWebServerApplicationContext -> GenericWebApplicationContext -> GenericApplicationContext -> AbstractApplicationContext -> DefaultResourceLoader 其中带上参数的是 GenericApplicationContext,如下初始化了 Context 中的 beanFactory 为 DefaultListableBeanFactory,接着是 AbstractApplicationContext 初始化了 resourcePatternResolver 创建 PathMatchingResourcePatternResolver 。最后是 DefaultResourceLoader 这里设置了默认的类加载器,即 AppClassLoader

获取 ExceptionReporter

这里获取 ExceptionReporter 的方式主要还是和之前 Listener 的方式一致,通过 getSpringFactoriesInstances 来获取所有的 SpringBootExceptionReporter

exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
                    new Class[] { ConfigurableApplicationContext.class }, context);

4.准备容器

其主要方法执行如下:

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
            SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
        // 为 ApplicationContext 设置 environment
        context.setEnvironment(environment);
        // 执行容器后置处理器
        postProcessApplicationContext(context);
        // 执行容器中的ApplicationContextInitializer
        applyInitializers(context);
         // 发送 ContextPrepareEvent,通知各个监听器。
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            // 打印启动新包括 pid 和 用户等。
            logStartupInfo(context.getParent() == null);
            // 打印 Profile 信息
            logStartupProfileInfo(context);
        }
        // Add boot specific singleton beans
        ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
        // 将启动参数作为 bean 注入到容器中
        beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
        if (printedBanner != null) {
            // 将banner 注入到容器中
            beanFactory.registerSingleton("springBootBanner", printedBanner);
        }
        if (beanFactory instanceof DefaultListableBeanFactory) {
            // 设置不允许定义同名的BeanDefinition,重复注册时抛出异常
            ((DefaultListableBeanFactory) beanFactory)
                    .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
        if (this.lazyInitialization) {
            // 如果是懒加载,则添加懒加载后置处理器。
            context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
        }
        // 获取启动类的参数
        Set<Object> sources = getAllSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        // 加载启动类,并将其注入到容器中
        load(context, sources.toArray(new Object[0]));
        // 发布 ApplicationPreparedEvent 事件
        listeners.contextLoaded(context);
    }

首先是设置 environment 信息,最终实现如下,为 AnnotationConfigServletWebServerApplicationContext 的刚才初始化的 reader 和 scanner 还有自己设置 environment信息。

public void setEnvironment(ConfigurableEnvironment environment) {
        super.setEnvironment(environment);
        this.reader.setEnvironment(environment);
        this.scanner.setEnvironment(environment);
    }

postProcessApplicationContext

其主要实现如下:
1、首先是指定 beanNameGenerator,默认情况下不会进入这里,在没有自定义 beanNameGenerator的情况下,AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner的默认实现是AnnotationBeanNameGenerator,即看是否有 value 定义值,假如没有就将首字母变成小写做为bean的名称。
2、查看是否存在resourceLoader 有的话就添加到 beanFactory 中。

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
        if (this.beanNameGenerator != null) {
            //假如存在 beanNameGenerator,将 beanNameGenerator 注册到容器中
            context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
                    this.beanNameGenerator);
        }
        if (this.resourceLoader != null) {
            if (context instanceof GenericApplicationContext) {
                ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
            }
            if (context instanceof DefaultResourceLoader) {
                ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
            }
        }
        if (this.addConversionService) {
            // 为 beanFactory 设置 conversionService 为 ApplicationConversionService
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }

执行 initializer

我们上面提到在初始化 SpringApplication 的时候会加载所有的 ApplicationContextInitializer,到这里就使用到了这些 initializer ,调用每个initializerinitialize 方法,并将 Context 作为参数传递进去。

protected void applyInitializers(ConfigurableApplicationContext context) {
        for (ApplicationContextInitializer initializer : getInitializers()) {
            Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
                    ApplicationContextInitializer.class);
            Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
            initializer.initialize(context);
        }
    }

我们 Debug 可以看到这里有 10 个 initializer,这里会包含部分 Spring Cloud 和 Apollo 的内容,我们只挑 Spring Boot 的部分进行讲解 :

initializer

1、DelegatingApplicationContextInitializer: 从environment中获取context.initializer.classes属性,默认为 null,可以使用多个使用逗号隔开,然后将调用这些类的 initialize 方法。
2、SharedMetadataReaderFactoryContextInitializer 主要是在 beanFactory 中添加一个CachingMetadataReaderFactoryPostProcessor 会在 refreshContext 中被执行。
3、ContextIdApplicationContextInitializer 将 Spring.application.name 作为 ContextId 设置到容器中。
4、ConfigurationWarningsApplicationContextInitializer 向beanFacotory 中注册一个 ConfigurationWarningsPostProcessor 作用是添加一下检查。默认有一个ComponentScanPackageCheck,作用是检查@ComponentScan扫描的包路径是否合法.
5、ServerPortInfoApplicationContextInitializerApplicationContext 中注册一个 ApplicationListener 用于监听WebServerInitializedEvent事件,向Environment中添加端口号local.sever.port。

  1. ConditionEvaluationReportLoggingListener 向容器中注册一个 ConditionEvaluationReportListener 主要用于打印日志。

执行 ApplicationPrepareContext 通知

目前 listeners#contextPrepared(context) 还是个空实现,还有接下来的打印日志,这里就不在详细描述。

load 加载

回到 prepareContext 的主流程,倒数第二部调用了 load 方法,主要如下:

protected void load(ApplicationContext context, Object[] sources) {
        // 打印日志
        if (logger.isDebugEnabled()) {
            logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
        }
        // 初始化 BeanDefinitionLoader
        BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
        // 假如 BeanDefinition 不为空,就将其设置到 loader 中。
        if (this.beanNameGenerator != null) {
            loader.setBeanNameGenerator(this.beanNameGenerator);
        }
        // 如果 resourceLoader  不为空,就将 resourceLoader 设置到 loader 中
        if (this.resourceLoader != null) {
            loader.setResourceLoader(this.resourceLoader);
        }
        // 如果 environment  不为空,就将 environment 设置到 loader 中
        if (this.environment != null) {
            loader.setEnvironment(this.environment);
        }
        // 调用 loader 的 load 方法
        loader.load();
    }

我们先来看一下 createBeanDefinitionLoader 方法:

BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
        Assert.notNull(registry, "Registry must not be null");
        Assert.notEmpty(sources, "Sources must not be empty");
        this.sources = sources;
        this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
        this.xmlReader = new XmlBeanDefinitionReader(registry);
        if (isGroovyPresent()) {
            this.groovyReader = new GroovyBeanDefinitionReader(registry);
        }
        this.scanner = new ClassPathBeanDefinitionScanner(registry);
        this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    }

主要做了两件事情:
1、设置 Reader ,包括 AnnotatedBeanDefinitionReaderXmlBeanDefinitionReader 假如是Groovy 环境就生成 GroovyBeanDefinitionReader
2、设置 Scanner ,主要是 ClassPathBeanDefinitionScanner ,然后检查 Application 中是否存在 ExcludeFilter ,有的话加入到 scanner 中。
接着其他的步骤都比较简单,我们直接来看一下 load 方法:

    int load() {
        int count = 0;
        for (Object source : this.sources) {
            count += load(source);
        }
        return count;
    }

这里的主要逻辑是遍历所有的 sources,这里的其实就是我们的 Main 类。最终调用了 load(Class<?> source) 方法,最终调用了 annotatedReader#register(source)方法。

5、刷新容器

刷新容器代码如下:

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

主要做两件事情:
1、假如需要注册关闭钩子的话,向 Context 注册关闭钩子。
2、调用 refresh 方法,刷新容器。
我们直接来看一下 refresh 方法,其最终调用了 AbstractApplicationContext 的 refresh 方法。其主要内容如下:

public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

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

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }

这个方法主要有如下步骤:
1、准备刷新容器。
2、初始化 BeanFactory。
3、对 BeanFactory 进行各种功能的填充,如对 @Autowrite 和 @Qualify 的支持就是这步加入的。
4、调用 postProcessBeanFactory 的扩展点。
5、激活各种 beanFactory 处理器。
6、注册拦截 bean 创建的 bean处理器,这里仅仅是创建而已,最后 getBean 的时候才会真正的调用。
7、初始化 Context 的 MessageSource,为一些国际化的内容。
8、初始化 ApplicationEventMulticaster 并放到 bean 工厂中。
9、扩展点,为其他的 Context 子类来初始化其 bean。
10、在所有的 bean 中找到 listener bean,并将其注册到广播器中。
11、初始化剩下的单例 (no-lazy-init)
12、完成刷新过程,并发出 ContextRefreshEvent 通知。
13、清除缓存。
我们接下来一步步解析这些流程。

5.1 准备刷新容器

protected void prepareRefresh() {
        // Switch to active.
        this.startupDate = System.currentTimeMillis();
        this.closed.set(false);
        this.active.set(true);

        if (logger.isDebugEnabled()) {
            if (logger.isTraceEnabled()) {
                logger.trace("Refreshing " + this);
            }
            else {
                logger.debug("Refreshing " + getDisplayName());
            }
        }

        // Initialize any placeholder property sources in the context environment.
        initPropertySources();

        // Validate that all properties marked as required are resolvable:
        // see ConfigurablePropertyResolver#setRequiredProperties
        getEnvironment().validateRequiredProperties();

        // Store pre-refresh ApplicationListeners...
        if (this.earlyApplicationListeners == null) {
            this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
        }
        else {
            // Reset local application listeners to pre-refresh state.
            this.applicationListeners.clear();
            this.applicationListeners.addAll(this.earlyApplicationListeners);
        }

        // Allow for the collection of early ApplicationEvents,
        // to be published once the multicaster is available...
        this.earlyApplicationEvents = new LinkedHashSet<>();
    }

上面代码比较简单,主要做了如下事情:
1、设置容器启动时间。
2、设置启动状态。
4、调用 initPropertySources 方法,调用到的是 GenericWebApplicationContextinitPropertySources 方法,最终调用如下方法:

public static void initServletPropertySources(MutablePropertySources sources,
            @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {

        Assert.notNull(sources, "'propertySources' must not be null");
        String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
        if (servletContext != null && sources.get(name) instanceof StubPropertySource) {
            sources.replace(name, new ServletContextPropertySource(name, servletContext));
        }
        name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
        if (servletConfig != null && sources.get(name) instanceof StubPropertySource) {
            sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
        }
    }
    1、如果 `servletContext` 不为空,且是 StubPropertySource 的子类,那么将其转为 `ServletContextPropertySource`.
    2、如果 `servletConfig` 不为空,且是 StubPropertySource 的子类,那么将其转为 `ServletContextPropertySource`.
      但是这里的  `servletContext`  和  `servletConfig`  都为空,所以不会进入。

4、将当前的 ApplicationListeners 放置到 earlyApplicationListeners 中。

5.2 初始化 BeanFactory

接着分析 obtainFreshBeanFactory 方法:

    protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        refreshBeanFactory();
        return getBeanFactory();
    }

主要做两件事情, refreshBeanFactory,初始化BeanFactory,最终调用了 GenericApplicationContext#refreshBeanFactory ,如下:

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());
    }

1、设置 refresh 的状态为 TRUE。
2、为 beanFactory 设置 setSerializationId ,这个里是 application,其主要由三段式组成 ApplicationName:profile:port。
接下来分析一下 getBeanFactory 方法:

    @Override
    public final ConfigurableListableBeanFactory getBeanFactory() {
        return this.beanFactory;
    }

最终还是调用了返回当前 contextbeanFactory,返回一个 DefaultListableBeanFactory 。

5.3prepareBeanFactory

这部分代码比较复杂,我们逐个看一下:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Tell the internal bean factory to use the context's class loader etc.
        beanFactory.setBeanClassLoader(getClassLoader());
        beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
        beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

        // Configure the bean factory with context callbacks.
        beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
        beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
        beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
        beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
        beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
        beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

        // BeanFactory interface not registered as resolvable type in a plain factory.
        // MessageSource registered (and found for autowiring) as a bean.
        beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
        beanFactory.registerResolvableDependency(ResourceLoader.class, this);
        beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
        beanFactory.registerResolvableDependency(ApplicationContext.class, this);

        // Register early post-processor for detecting inner beans as ApplicationListeners.
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

        // Detect a LoadTimeWeaver and prepare for weaving, if found.
        if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            // Set a temporary ClassLoader for type matching.
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }

        // Register default environment beans.
        if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
        }
        if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
            beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
        }
    }

1、为 beanFactory 设置类加载器,为当前 context 的类加载器。
2、设置 beanFactory 的 BeanExpressionResolverStandardBeanExpressionResolver
3、beanFactory增加一个默认的 PropertyEditor,主要用于对 bean 的属性设置进行管理。
4、为 beanFactory 增加一个 BeanPostProcessor 为 ApplicationContextAwareProcessor
5、将 EnvironmentAware、EmbeddedValueResolverAware、ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、添加到忽略自动装配的接口中。,当spring将ApplicationContextAwareProcessor注册后,那么在invokeAwareInterfaces中直接,调用的Aware类已经不是普通的bean了,如ResourceLoaderAware,那么需要在spring做bean的依赖注入时忽略它们。
6、将当前 Context 注册为解析如下依赖的注入对象,包括 BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。比如说我们调用 @Autowrite 注入 ApplicationContext 就是注入当前的 Context。
7、注册 BeanPostProcessor , ApplicationListenerDetector 。
8、添加默认的系统环境bean。

5.4postProcessBeanFactory

该方法最终调用了子类的 AnnotationConfigServletWebApplicationContext#postProcessBeanFactory ,

@Override
    protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.postProcessBeanFactory(beanFactory);
        if (!ObjectUtils.isEmpty(this.basePackages)) {
            this.scanner.scan(this.basePackages);
        }
        if (!this.annotatedClasses.isEmpty()) {
            this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
        }
    }

主要做了三件事情:
1、为 BeanFactory 设置了一个为ServletContextAwareProcessor 类型的 BeanPostProcessor,并设置了忽略接口ServletContextAware.
2、假如basePackage 大于 0 的话,就调用 scanner 的 scan 方法。
3、如果 annotatedClasses 大于 0 的话,就调用 AnnotatedBeanDefinitionReader 的 register 方法。

5.5 激活各种 bean 处理器

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
        PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

        // Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
        // (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
        if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
            beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
            beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
        }
    }

1、调用PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors.
2、如果beanFactory.getTempClassLoader() 等于null并且 beanFactory含有loadTimeWeaver的定义的话,就向beanFactory添加一个LoadTimeWeaverAwareProcessor,然后设置TempClassLoader 为 ContextTypeMatchClassLoader.
其中第一步获得的BeanFactoryPostProcessor 有如下:

BeanFactoryPostProcessor

其中最重要的就是调用 invokeBeanDefinitionRegistryPostProcessors 方法中,调用了 ConfigurationClassPostProcessor 大家看看如下 链接,看详解,主要负责加载大部分的 BeanDefinition 注册到 registry 中。

5.6 registerBeanPostProcessors 方法

这个方法最终调用了 PostProcessorRegistrationDelegate#registerBeanPostProcessors,如下:

public static void registerBeanPostProcessors(
            ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

        String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

        // Register BeanPostProcessorChecker that logs an info message when
        // a bean is created during BeanPostProcessor instantiation, i.e. when
        // a bean is not eligible for getting processed by all BeanPostProcessors.
        int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
        beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

        // Separate between BeanPostProcessors that implement PriorityOrdered,
        // Ordered, and the rest.
        List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
        List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
        List<String> orderedPostProcessorNames = new ArrayList<>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<>();
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
                priorityOrderedPostProcessors.add(pp);
                if (pp instanceof MergedBeanDefinitionPostProcessor) {
                    internalPostProcessors.add(pp);
                }
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
            }
        }

        // First, register the BeanPostProcessors that implement PriorityOrdered.
        sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

        // Next, register the BeanPostProcessors that implement Ordered.
        List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
        for (String ppName : orderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            orderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        sortPostProcessors(orderedPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, orderedPostProcessors);

        // Now, register all regular BeanPostProcessors.
        List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
        for (String ppName : nonOrderedPostProcessorNames) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            nonOrderedPostProcessors.add(pp);
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

        // Finally, re-register all internal BeanPostProcessors.
        sortPostProcessors(internalPostProcessors, beanFactory);
        registerBeanPostProcessors(beanFactory, internalPostProcessors);

        // Re-register post-processor for detecting inner beans as ApplicationListeners,
        // moving it to the end of the processor chain (for picking up proxies etc).
        beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
    }

1、先从 beanFactory 中获取 BeanPostProcessor 类型的 bean。其中有如下 BeanPostProcessor


BeanPostProcessor

2、添加一个BeanPostProcessor , BeanPostProcessorChecker ,主要用于日志打印。
3、遍历所有的 postProcessorNames :
(1)将所有实现了 PriorityOrdered 接口的 bean 放到 priorityOrderedPostProcessors 中。
(2)如果bean 即实现了 PriorityOrdered 接口,也实现了 MergedBeanDefinitionPostProcessor 接口的话,将其放到 internalPostProcessors 中。
(3)假如 bean 实现了 Ordered 接口放到 orderedPostProcessorNames 中。
(4)假如都没有,就放到 nonOrderedPostProcessorNames 中。
4、注册 priorityOrderedPostProcessors 的 BPP,有如下:


image.png

5、注册 orderedPostProcessors 的 BPP,有如下:
image.png

6、注册所有 nonOrderedPostProcessors 的 BPP 有如下:


image.png

7、 注册所有MergedBeanDefinitionPostProcessor类型的BeanPostProcessor,并非是重复注册.如下:
8、在最后新增一个BPP 是 ApplicationListenerDetector。

5.7 initMessageSource 方法

protected void initMessageSource() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
            this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
            // Make MessageSource aware of parent MessageSource.
            if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
                HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
                if (hms.getParentMessageSource() == null) {
                    // Only set parent context as parent MessageSource if no parent MessageSource
                    // registered already.
                    hms.setParentMessageSource(getInternalParentMessageSource());
                }
            }
            if (logger.isTraceEnabled()) {
                logger.trace("Using MessageSource [" + this.messageSource + "]");
            }
        }
        else {
            // Use empty MessageSource to be able to accept getMessage calls.
            DelegatingMessageSource dms = new DelegatingMessageSource();
            dms.setParentMessageSource(getInternalParentMessageSource());
            this.messageSource = dms;
            beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
            if (logger.isTraceEnabled()) {
                logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
            }
        }
    }

其中的主要流程如下: HierarchicalMessageSource
1、从 beanFactory 中读取 messageSource ,看是否存在,假如存在,获取之,然后判断是是HierarchicalMessageSource 类型假如是,就将其 ParentMessageSource 设置为 nternalParentMessageSource。
2、如果不存在,就实例化 DelegatingMessageSource 作为 getInternalParentMessageSource 调用的结果。

5.8 初始化 ApplicationEventMulticaster

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.isTraceEnabled()) {
                logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
            }
        }
        else {
            this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
            beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
            if (logger.isTraceEnabled()) {
                logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                        "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
            }
        }
    }

这里的逻辑主要是 如果存在用户自定义的广播器,那么就将其设置为默认广播器。假如不存在就初始化 SimpleApplicationEventMulticaster 作为默认的广播器。

5.9 Onrefresh

这个接口是留给子类的扩展点 ServletWebServerApplicationContext 的代码如下:

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

1 、先调用父类的 onRefresh 方法,最终代码如下:

public static ThemeSource initThemeSource(ApplicationContext context) {
        if (context.containsLocalBean(THEME_SOURCE_BEAN_NAME)) {
            ThemeSource themeSource = context.getBean(THEME_SOURCE_BEAN_NAME, ThemeSource.class);
            // Make ThemeSource aware of parent ThemeSource.
            if (context.getParent() instanceof ThemeSource && themeSource instanceof HierarchicalThemeSource) {
                HierarchicalThemeSource hts = (HierarchicalThemeSource) themeSource;
                if (hts.getParentThemeSource() == null) {
                    // Only set parent context as parent ThemeSource if no parent ThemeSource
                    // registered already.
                    hts.setParentThemeSource((ThemeSource) context.getParent());
                }
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Using ThemeSource [" + themeSource + "]");
            }
            return themeSource;
        }
        else {
            // Use default ThemeSource to be able to accept getTheme calls, either
            // delegating to parent context's default or to local ResourceBundleThemeSource.
            HierarchicalThemeSource themeSource = null;
            if (context.getParent() instanceof ThemeSource) {
                themeSource = new DelegatingThemeSource();
                themeSource.setParentThemeSource((ThemeSource) context.getParent());
            }
            else {
                themeSource = new ResourceBundleThemeSource();
            }
            if (logger.isDebugEnabled()) {
                logger.debug("Unable to locate ThemeSource with name '" + THEME_SOURCE_BEAN_NAME +
                        "': using default [" + themeSource + "]");
            }
            return themeSource;
        }
    }

1、如果context中有themeSource的定义
(1)从context 获取,id 为themeSource type为ThemeSource 的 bean
(2)如果父容器实现了ThemeSource,并且ThemeSource 是HierarchicalThemeSource 的子类,并且HierarchicalThemeSource 的ParentThemeSource 没有进行设置.则将父容器赋值给HierarchicalThemeSource的ParentThemeSource
2、如果context中没有themeSource的定义
(1)如果父容器为ThemeSource的子类,则实例化DelegatingThemeSource,并将父容器赋值给DelegatingThemeSource的ParentThemeSource
(2)否则实例化为DelegatingThemeSource

2、调用完父类的 Onfresh 后,创建一个嵌入的Servlet容器.这个我们后续分析.

5.10 registerListeners 注册监听器

这个方法的主要作用是初始化所有的 listener

protected void registerListeners() {
        // Register statically specified listeners first.
        for (ApplicationListener<?> listener : getApplicationListeners()) {
            getApplicationEventMulticaster().addApplicationListener(listener);
        }

        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let post-processors apply to them!
        String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
        for (String listenerBeanName : listenerBeanNames) {
            getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
        }

        // Publish early application events now that we finally have a multicaster...
        Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
        this.earlyApplicationEvents = null;
        if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
            for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
                getApplicationEventMulticaster().multicastEvent(earlyEvent);
            }
        }
    }

1、硬编码方式注册的监听器添加到SimpleApplicationEventMulticaster中的defaultRetriever的applicationListeners. 其中有如下 15个:


image.png

2、将注册到配置文件中的 ApplicationListener 找出来,并添加到SimpleApplicationEventMulticaster中的defaultRetriever。有如下 5 个


image.png

3、 将之前发生的 earlyApplicationEvents 重复发送一遍。

5.11 finishBeanFactoryInitialization

该方法主要作用是 初始化剩余的单例(non-lazy-init))

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
        // Initialize conversion service for this context.
        if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
                beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
            beanFactory.setConversionService(
                    beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
        }

        // Register a default embedded value resolver if no bean post-processor
        // (such as a PropertyPlaceholderConfigurer bean) registered any before:
        // at this point, primarily for resolution in annotation attribute values.
        if (!beanFactory.hasEmbeddedValueResolver()) {
            beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
        }

        // Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
        String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
        for (String weaverAwareName : weaverAwareNames) {
            getBean(weaverAwareName);
        }

        // Stop using the temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(null);

        // Allow for caching all bean definition metadata, not expecting further changes.
        beanFactory.freezeConfiguration();

        // Instantiate all remaining (non-lazy-init) singletons.
        beanFactory.preInstantiateSingletons();
    }

1、如果 beaFactory 中存在 CONVERSION_SERVICE_BEAN_NAME name 的 bean,并且类型为 ConversionService.class ,将其设置到 beanFactory 中。
2、如果 beanFactory 中没有 EmbeddedValueResolver,添加一个。
3、设置 type 为 LoadTimeWeaverAware 的bean。
4、设置TempClassLoader 为null
5、冻结所有 bean 的定义,也就是从这里开始,所有的 bean 后面都不允许被修改了。
6、初始化剩下的单实例. 后续进行解析。

5.12 finishRefresh

protected void finishRefresh() {
        // Clear context-level resource caches (such as ASM metadata from scanning).
        clearResourceCaches();

        // Initialize lifecycle processor for this context.
        initLifecycleProcessor();

        // Propagate refresh to lifecycle processor first.
        getLifecycleProcessor().onRefresh();

        // Publish the final event.
        publishEvent(new ContextRefreshedEvent(this));

        // Participate in LiveBeansView MBean, if active.
        LiveBeansView.registerApplicationContext(this);
    }

1、清理 resource caches。
2、初始化LifecycleProcessor.
3、调用 LifecycleProcessor 的 onrefresh 方法。
4、发布ContextRefreshedEvent 事件.

5.13 resetCommonCaches

protected void resetCommonCaches() {
        ReflectionUtils.clearCache();
        AnnotationUtils.clearCache();
        ResolvableType.clearCache();
        CachedIntrospectionResults.clearClassLoader(getClassLoader());
    }

1、清除 ReflectionUtils 缓存。
2、清除 AnnotationUtils 缓存。
3、清除 ResolvableType 缓存。
至此 我们分析完了 Onfresh 的所有工作。

6、afterRefresh Spring 容器的后置处理器

目前这个方法的实现是空的。

7、通知所有 listener 结束启动

这里最终调用了 EventPublishingRunListener#started 方法:

public void started(ConfigurableApplicationContext context) {
        context.publishEvent(new ApplicationStartedEvent(this.application, this.args, context));
        AvailabilityChangeEvent.publish(context, LivenessState.CORRECT);
    }

1、首先是调用 context.publishEvent 来发布启动完成事件。其中监听的 listener 如下:


image.png

2、调用 AvailabilityChangeEvent 发布 CORRECT 事件,代表启动成功。

8、调用所有 runner 的 run 方法

private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List<Object> runners = new ArrayList<>();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet<>(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }

如上,流程比较简单,首先查找所有的 ApplicationRunner 和 CommandLineRunner ,然后遍历调用他们的 run 方法。

9、通知所有 listener running 事件。

最终处理这个事件的有如下两个 Listener


image.png

至此,我们对 SpringBoot 的处理流程已经基本分析完成了,我们接下来分析一下 错误的处理流程:

处理错误

其主要方法是 handleRunFailure

private void handleRunFailure(ConfigurableApplicationContext context, Throwable exception,
            Collection<SpringBootExceptionReporter> exceptionReporters, SpringApplicationRunListeners listeners) {
        try {
            try {
                handleExitCode(context, exception);
                if (listeners != null) {
                    listeners.failed(context, exception);
                }
            }
            finally {
                reportFailure(exceptionReporters, exception);
                if (context != null) {
                    context.close();
                }
            }
        }
        catch (Exception ex) {
            logger.warn("Unable to close ApplicationContext", ex);
        }
        ReflectionUtils.rethrowRuntimeException(exception);
    }

1、生成 exitCode
2、发送ApplicationFailedEvent 事件。

public void failed(ConfigurableApplicationContext context, Throwable exception) {
        ApplicationFailedEvent event = new ApplicationFailedEvent(this.application, this.args, context, exception);
        if (context != null && context.isActive()) {
            // Listeners have been registered to the application context so we should
            // use it at this point if we can
            context.publishEvent(event);
        }
        else {
            // An inactive context may not have a multicaster so we use our multicaster to
            // call all of the context's listeners instead
            if (context instanceof AbstractApplicationContext) {
                for (ApplicationListener<?> listener : ((AbstractApplicationContext) context)
                        .getApplicationListeners()) {
                    this.initialMulticaster.addApplicationListener(listener);
                }
            }
            this.initialMulticaster.setErrorHandler(new LoggingErrorHandler());
            this.initialMulticaster.multicastEvent(event);
        }
    }

1、获取该 ApplicationFailedEvent 事件,如果该错误时发生在 Afterfresh 之后的,那么 Context 就是已经激活了,那么就直接调用 context#publishEvent(event) 发送时间即可。
2、假如不是,比如说在 refresh 的时候发生的,那么就不知道在 ApplicationContext中是否配置了 ApplicationListener ,所以这里直接重新配置 ApplicationListener,最后发送事件。
最后我们来看一下他发送了什么事件:


image.png

3、接下来调用完成 listener 后,打印异常堆栈,如下:

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
        try {
            for (SpringBootExceptionReporter reporter : exceptionReporters) {
                if (reporter.reportException(failure)) {
                    registerLoggedException(failure);
                    return;
                }
            }
        }
        catch (Throwable ex) {
            // Continue with normal handling of the original failure
        }
        if (logger.isErrorEnabled()) {
            logger.error("Application run failed", failure);
            registerLoggedException(failure);
        }
    }

1、遍历所有的 exceptionReporters ,如果 reporter#reportException 返回 true ,就调用 registerLoggedException 方法,注册错误信息,最终调用 SpringBootExceptionHandler 注册 错误信息。

4、调用 Context 的 close 方法:

public void close() {
        synchronized (this.startupShutdownMonitor) {
            doClose();
            // If we registered a JVM shutdown hook, we don't need it anymore now:
            // We've already explicitly closed the context.
            if (this.shutdownHook != null) {
                try {
                    Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
                }
                catch (IllegalStateException ex) {
                    // ignore - VM is already shutting down
                }
            }
        }
    }

1、doClose 先判断当前 Context 是否是 Active ,如果不是,就调用 supre.close ,假如是就发送 REFUSING_TRAFFIC 事件。
2、删除所有的 shutdownHook。

5、最后调用 ReflectionUtils#rethrowRuntimeException 将错误重新抛出来。

public static void rethrowRuntimeException(Throwable ex) {
        if (ex instanceof RuntimeException) {
            throw (RuntimeException) ex;
        }
        if (ex instanceof Error) {
            throw (Error) ex;
        }
        throw new UndeclaredThrowableException(ex);
    }

如上,先判断是否是 RuntimeException 类型,假如是就直接抛出来,假如不是就重新包装一下,作为 UndeclaredThrowableException 抛出。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,948评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,371评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,490评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,521评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,627评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,842评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,997评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,741评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,203评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,534评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,673评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,339评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,955评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,770评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,000评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,394评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,562评论 2 349

推荐阅读更多精彩内容