main方法进入

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

ioc_1.png

底下的方法分为两步:new SpringApplication(primarySources):创建SpringApplicationrun():启动SpringApplication

1.创建SpringApplication

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // resourceLoader为null
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    // 将传入的DemoApplication启动类放入primarySources中,这样应用就知道主启动类在哪里,叫什么了
    // SpringBoot一般称呼这种主启动类叫primarySource(主配置资源来源)
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    // 判断当前应用环境[Reactive环境(全NIO)/ Servlet(WebMvc)环境/非Web环境]
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    // 1.1 设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    // 1.2 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    // 1.3 确定主配置类
    this.mainApplicationClass = deduceMainApplicationClass();
}

1.1 setInitializers:设置初始化器

它是在IOC容器之前的回调。它的使用方式有三种:

  • 运行SpringApplication之前手动添加
public class ApplicationContextInitializerDemo implements ApplicationContextInitializer {
    
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        System.out.println("ApplicationContextInitializerDemo#initialize run...");
    }
    
}


@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        // SpringApplication.run(DemoApplication.class, args);
        SpringApplication springApplication = new SpringApplication(DemoApplication.class);
        springApplication.addInitializers(new ApplicationContextInitializerDemo());
        springApplication.run(args);
    }

}
  • application.properties中配置
    context.initializer.classes=com.example.demo.ApplicationContextInitializerDemo
  • spring.factories中配置
    在工程的 resources 目录下新建 “META-INF” 目录,并在下面创建一个 spring.factories 文件。在文件内声明:

org.springframework.context.ApplicationContextInitializer=com.example.demo.ApplicationContextInitializerDemo

内置Initializer

spring-boot 和 spring-boot-autoconfigure 包下的 spring.factories 里面对于 ApplicationContextInitializer 的配置:

# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\ // 报告IOC容器的一些常见的错误配置
org.springframework.boot.context.ContextIdApplicationContextInitializer,\ // 设置Spring应用上下文的ID
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\ // 加载 application.properties 中 context.initializer.classes 配置的类
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer // 将内置servlet容器实际使用的监听端口写入到 Environment 环境属性中
# Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ // 创建一个 SpringBoot 和 ConfigurationClassPostProcessor 共用的 CachingMetadataReaderFactory 对象
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener // 将 ConditionEvaluationReport 写入日志

1.2 setListeners:设置监听器

用于监听IOC容器中发布的各种事件,到后续看IOC容器的刷新过程时可以看到。

// 加载所有类型为ApplicationListener的已配置的组件的全限定类名
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

内置Listener

# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\ // 应用上下文加载完成后对缓存做清除工作
org.springframework.boot.builder.ParentContextCloserApplicationListener,\ // 监听双亲应用上下文的关闭事件并往自己的子应用上下文中传播
org.springframework.boot.context.FileEncodingApplicationListener,\ // 检测系统文件编码与应用环境编码是否一致,如果系统文件编码和应用环境的编码不同则终止应用启动
org.springframework.boot.context.config.AnsiOutputApplicationListener,\ // 根据 spring.output.ansi.enabled 参数配置 AnsiOutput
org.springframework.boot.context.config.ConfigFileApplicationListener,\ // 从常见的那些约定的位置读取配置文件
org.springframework.boot.context.config.DelegatingApplicationListener,\ // 监听到事件后转发给 application.properties 中配置的 context.listener.classes 的监听器
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\ // 对环境就绪事件 ApplicationEnvironmentPreparedEvent 和应用失败事件 ApplicationFailedEvent 做出响应
org.springframework.boot.context.logging.LoggingApplicationListener,\ // 配置 LoggingSystem。使用 logging.config 环境变量指定的配置或者缺省配置
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener // 使用一个可以和 SpringBoot 可执行jar包配合工作的版本替换 LiquibaseServiceLocator

1.3 确定主配置类

private Class<?> deduceMainApplicationClass() {
    try {
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            // 从本方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类
            if ("main".equals(stackTraceElement.getMethodName())) {
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // Swallow and continue
    }
    return null;
}

源码很简单,从 deduceMainApplicationClass 方法开始往上爬,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。

2.run():启动SpringApplication

ioc_2.png

public ConfigurableApplicationContext run(String... args) {
    // 创建StopWatch对象,用来监控启动时间
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    // 创建空的IOC容器,和一组异常报告器、配置与awt相关的信息
    ConfigurableApplicationContext context = null;
    Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
    configureHeadlessProperty();
    // 2.1 获取SpringApplicationRunListeners,并调用starting方法(回调机制)
    SpringApplicationRunListeners listeners = getRunListeners(args);
    // 【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
    listeners.starting();
    try {
        // 将main方法的args参数封装到一个对象中
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        // 2.2 准备运行时环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        // 如果有配置 spring.beaninfo.ignore,则将该配置设置进系统参数
        configureIgnoreBeanInfo(environment);
        // 打印SpringBoot的banner
        Banner printedBanner = printBanner(environment);
        // 2.3 创建ApplicationContext
        context = createApplicationContext();
        // 初始化异常报告器
        exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
        new Class[] { ConfigurableApplicationContext.class }, context);
        // 2.4 初始化IOC容器
        prepareContext(context, environment, listeners, applicationArguments, printedBanner);
        // 2.5 刷新容器,最核心的部分
        refreshContext(context);
        // 刷新后的处理,空方法
        afterRefresh(context, applicationArguments);
        stopWatch.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
        }
        // 【回调】发布started事件,事件的发布还会影响到父容器
        listeners.started(context);
        // 运行器回调:从容器中获取了ApplicationRunner和CommandLineRunner,已标记废弃
        callRunners(context, applicationArguments);

        return context;
    } catch (Throwable var9) {
      this.handleRunFailure(context, listeners, (FailureAnalyzers)analyzers, var9);
      throw new IllegalStateException(var9);
    }   
}

2.1 getRunListeners:获取SpringApplicationRunListeners

依然是SPI机制,取spring.factories中所有SpringApplicationRunListener。

/**
 * Listener for the SpringApplication code run method.
 * SpringApplicationRunListeners are loaded via the SpringFactoriesLoader
 * and should declare a public constructor that accepts a SpringApplication
 * instance and a String[] of arguments. A new
 * SpringApplicationRunListener instance will be created for each run.
 *
 * 监听SpringApplication运行方法。
 * SpringApplication是SpringFactoriesLoader,应该声明一个接受SpringApplication实例和String[]参数的公共构造函数。
 * 将为每次运行创建一个新的SpringApplicationRunListener的instance。
 */
public interface SpringApplicationRunListener {

    /**
     * Called immediately when the run method has first started. Can be used for very
     * early initialization.
     * 首次启动run方法时立即调用。可用于非常早期的初始化。
     */
    void starting();

    /**
     * Called once the environment has been prepared, but before the
     * ApplicationContext has been created.
     * 准备好环境(Environment构建完成),但在创建ApplicationContext之前调用。
     */
    void environmentPrepared(ConfigurableEnvironment environment);

    /**
     * Called once the ApplicationContext has been created and prepared, but
     * before sources have been loaded.
     * 在创建和构建ApplicationContext之后,但在加载之前调用。
     */
    void contextPrepared(ConfigurableApplicationContext context);

    /**
     * Called once the application context has been loaded but before it has been
     * refreshed.
     * ApplicationContext已加载但在刷新之前调用。
     */
    void contextLoaded(ConfigurableApplicationContext context);

    /**
     * The context has been refreshed and the application has started but
     * CommandLineRunners and ApplicationRunners have not been called.
     * @since 2.0.0
     * ApplicationContext已刷新,应用程序已启动,但尚未调用CommandLineRunners和ApplicationRunners。
     */
    void started(ConfigurableApplicationContext context);

    /**
     * Called immediately before the run method finishes, when the application context has
     * been refreshed and all CommandLineRunners and ApplicationRunners have been called.
     * @since 2.0.0
     * 在运行方法彻底完成之前立即调用,刷新ApplicationContext并调用所有CommandLineRunners和ApplicationRunner。
     */
    void running(ConfigurableApplicationContext context);

    /**
     * Called when a failure occurs when running the application.
     * @since 2.0.0
     * 在运行应用程序时失败时调用。
     */
    void failed(ConfigurableApplicationContext context, Throwable exception);
}

后续IOC启动过程中会常出现这些SpringApplicationRunListeners的身影,我们可以多加留意。
默认情况下加载的listeners有一个,类型为EventPublishingRunListener

回到 run 方法中:

//......
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(); //【回调】首次启动run方法时立即调用。可用于非常早期的初始化(准备运行时环境之前)。
    try {
        // 将main方法的args参数封装到一个对象中
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
        //......

在 prepareEnvironment 之前,run方法中调用了:listeners.starting() ,已经开始了事件回调。

2.2 prepareEnvironment:准备运行时环境

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments) {
    // Create and configure the environment
    // 创建运行时环境
    // 根据当前的应用运行环境类型,创建不同的 Environment 。默认 SpringBoot 环境下会创建 StandardServletEnvironment 。    
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    // 配置运行时环境
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    // 【回调】SpringApplicationRunListener的environmentPrepared方法(Environment构建完成,但在创建ApplicationContext之前)
    listeners.environmentPrepared(environment);
    // 环境与应用绑定
    bindToSpringApplication(environment);
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    ConfigurationPropertySources.attach(environment);
    return environment;
}

2.3 createApplicationContext:创建IOC容器

public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
        + "annotation.AnnotationConfigApplicationContext";
public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
        + "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
public static final String DEFAULT_REACTIVE_WEB_CONTEXT_CLASS = "org.springframework."
        + "boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext";

protected ConfigurableApplicationContext createApplicationContext() {
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            // 根据Web应用类型决定实例化哪个IOC容器
            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);
}

可以发现都是创建的基于Annotation的 ApplicationContext。

注意,BeanFactory 在这里已经被创建了:

public GenericApplicationContext() {
    this.beanFactory = new DefaultListableBeanFactory();
}

对三种类型的运行时环境、IOC容器的类型归纳一下:

  • Servlet - StandardServletEnvironment - AnnotationConfigServletWebServerApplicationContext
  • Reactive - StandardReactiveWebEnvironment - AnnotationConfigReactiveWebServerApplicationContext
  • None - StandardEnvironment - AnnotationConfigApplicationContext

2.4 prepareContext:初始化IOC容器

private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
        SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
    // 将创建好的应用环境设置到IOC容器中
    context.setEnvironment(environment);
    // IOC容器的后置处理
    postProcessApplicationContext(context);
    // 执行Initializer
    applyInitializers(context);
    // 【回调】SpringApplicationRunListeners的contextPrepared方法(在创建和准备ApplicationContext之后,但在加载之前)
    listeners.contextPrepared(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    // Add boot specific singleton beans
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    // 创建两个组件:在控制台打印Banner的,之前把main方法中参数封装成对象的组件
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory)
                .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    // Load the sources
    // 加载主启动类
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    // 注册主启动类
    load(context, sources.toArray(new Object[0]));
    // 【回调】SpringApplicationRunListeners的contextLoaded方法(ApplicationContext已加载但在刷新之前)
    listeners.contextLoaded(context);
}

postProcessApplicationContext:IOC容器的后置处理

public static final String CONFIGURATION_BEAN_NAME_GENERATOR =
            "org.springframework.context.annotation.internalConfigurationBeanNameGenerator";

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
    // 注册BeanName生成器
    if (this.beanNameGenerator != null) {
        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) {
        context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
    }
}

它设置了几个组件:

  • 如果 beanNameGenerator 不为空,则把它注册到IOC容器中。 BeanNameGenerator 是Bean的name生成器,指定的 CONFIGURATION_BEAN_NAME_GENERATOR 在修改首字母大写后无法从IDEA索引到,暂且放置一边。
  • ResourceLoader 和 ClassLoader,这些都在前面准备好了
  • ConversionService,用于类型转换的工具,前面也准备好了,并且还做了容器共享

applyInitializers:执行Initializer

这个方法会获取到所有 Initializer,调用initialize方法。
而这些 Initializer,其实就是刚创建 SpringApplication 时准备的那些 ApplicationContextInitializer。见1.1

加载、注册主启动类 等预处理工作

protected void load(ApplicationContext context, Object[] sources) {
    if (logger.isDebugEnabled()) {
        logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources));
    }
    BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources);
    // 设置BeanName生成器,通过Debug发现此时它还没有被注册
    if (this.beanNameGenerator != null) {
        loader.setBeanNameGenerator(this.beanNameGenerator);
    }
    // 设置资源加载器
    if (this.resourceLoader != null) {
        loader.setResourceLoader(this.resourceLoader);
    }
    // 设置运行环境
    if (this.environment != null) {
        loader.setEnvironment(this.environment);
    }
    loader.load();
}

将启动类包装为BeanDefinition注册到IOC容器中,调用BeanDefinitionReaderUtils的registerBeanDefinition方法。

2.5 refreshContext:刷新容器

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

它直接调了refresh方法(注意此时还是 SpringApplication,没有进到真正的IOC容器),后面又注册了一个关闭的钩子。
作用是监听JVM关闭时销毁IOC容器和里面的Bean。

下面我们来看IOC容器启动时最核心的refresh方法:

这个方法很长,也很重要,分为十三步。

// 最终调到AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // ...
        try {
            // Prepare this context for refreshing.
            // 1 初始化前的预处理
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
            // 2 获取BeanFactory,加载所有bean的定义信息(未实例化)
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            // 3 BeanFactory的预处理配置
            prepareBeanFactory(beanFactory);
            // Allows post-processing of the bean factory in context subclasses.
            // 4 BeanFactory的后置处理
            postProcessBeanFactory(beanFactory);
    
            // Invoke factory processors registered as beans in the context.
            // 5 执行BeanFactory创建后的后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            // Register bean processors that intercept bean creation.
            // 6 注册Bean的后置处理器
            registerBeanPostProcessors(beanFactory);
    
            // Initialize message source for this context.
            // 7 初始化MessageSource(SpringMVC)
            initMessageSource();
    
            // Initialize event multicaster for this context.
            // 8 初始化事件派发器
            initApplicationEventMulticaster();
    
            // Initialize other special beans in specific context subclasses.
            // 9 子类的多态onRefresh
            onRefresh();
    
            // Check for listener beans and register them.
            // 10 注册监听器
            registerListeners();

            // 到此为止,BeanFactory已创建完成

            // Instantiate all remaining (non-lazy-init) singletons.
            // 11 初始化剩余的单实例Bean
            finishBeanFactoryInitialization(beanFactory);
    
            // 12 完成容器的创建工作
            finishRefresh();
        }
        catch (BeansException ex) {
            // ...
        }

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

其中主要有三个逻辑,我们慢慢看:

ioc_3.png

invokeBeanFactoryPostProcessors:执行容器扩展点

onRefresh:

finishBeanFactoryInitialization:

Last modification:May 16, 2021
如果觉得我的文章对你有用,请随意赞赏