接下来我们从源码的角度来看下SpringBoot启动的时候到底做了那些事情?

启动类

这里我们使用的启动类的代码如下:

1public static void main(String[] args) {
2    SpringApplication.run(SpringBootApplicationG.class, args);
3}

SpringBoot的启动入口是main方法中的run()方法,其中SpringBootApplicationG.class是一个配置类,通常情况下设置为当前类,args为命令行启动的时候传入的参数。

进入run方法之后,一直往下走会到下面的方法中:

1public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
2    // 构造SpringApplication对象
3    return new SpringApplication(primarySources).run(args);
4}

从上面的方法可以看出其实我们可以直接在run方法中使用上面的代码,这样我们还可以对ApplicationConttext做自己的定义。

1public static void main(String[] args) {
2    SpringApplication springApplication = new SpringApplication(SpringBootApplicationG.class);
3    springApplication.setBannerMode(...);
4    springApplication.setBanner(...);
5    springApplication.run(args);
6
7    //		SpringApplication.run(SpringBootApplicationG.class, args);
8}

这样写扩展性更强一点。

接着我们进入SpringApplication()的构造方法中看看,然后在看看run()方法。

 1public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
 2    this.resourceLoader = resourceLoader;
 3
 4    Assert.notNull(primarySources, "PrimarySources must not be null");
 5
 6    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
 7
 8    // 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
 9    this.webApplicationType = WebApplicationType.deduceFromClasspath();
10
11    // 2. 从spring.factories中获取BootstrapRegistryInitializer对象
12    this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
13
14    // 3. 从spring.factories中获取ApplicationContextInitializer对象
15    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
16
17    // 4. 从spring.factories中获取ApplicationListener对象
18    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
19
20    // 5. 推测出Main类(main()方法所在的类)
21    this.mainApplicationClass = deduceMainApplicationClass();
22}

上面的代码主要干了三件事情:推断应用类型、从spring.factories中加载一些初始化器、推断Main类。

推断应用类型

 1private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
 2private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
 3private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };
 4
 5
 6static WebApplicationType deduceFromClasspath() {
 7    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
 8        return WebApplicationType.REACTIVE;
 9    }
10    for (String className : SERVLET_INDICATOR_CLASSES) {
11        if (!ClassUtils.isPresent(className, null)) {
12            return WebApplicationType.NONE;
13        }
14    }
15    return WebApplicationType.SERVLET;
16}
  1. 如果ClassPath中既没有DispatcherServlet,没有DispatcherHandler类,那么就是REACTIVE应用;
  2. 接下来,如果ClassPath中不存在ServletConfigurableWebApplicationContext类,则应用类型为NONE;
  3. 否则就是SERVLET应用

加载初始化器

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

ApplicationContextInitializer对应的spring.factories中的value:

1# Application Context Initializers
2org.springframework.context.ApplicationContextInitializer=\
3org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
4org.springframework.boot.context.ContextIdApplicationContextInitializer,\
5org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
6org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
7org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer

加载监听器

记在一些默认的监听器:

1# Application Listeners
2org.springframework.context.ApplicationListener=\
3org.springframework.boot.ClearCachesApplicationListener,\
4org.springframework.boot.builder.ParentContextCloserApplicationListener,\
5org.springframework.boot.context.FileEncodingApplicationListener,\
6org.springframework.boot.context.config.AnsiOutputApplicationListener,\
7org.springframework.boot.context.config.DelegatingApplicationListener,\
8org.springframework.boot.context.logging.LoggingApplicationListener,\
9org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

其中的EnvironmentPostProcessorApplicationListener比较重要。

推断Main类

 1private Class<?> deduceMainApplicationClass() {
 2    try {
 3        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
 4        for (StackTraceElement stackTraceElement : stackTrace) {
 5            if ("main".equals(stackTraceElement.getMethodName())) {
 6                return Class.forName(stackTraceElement.getClassName());
 7            }
 8        }
 9    } catch (ClassNotFoundException ex) {
10        // Swallow and continue
11    }
12    return null;
13}

看调用栈中的main方法在哪个类,Main类就是哪个类。

Run()方法

 1public ConfigurableApplicationContext run(String... args) {
 2    long startTime = System.nanoTime();
 3
 4    // 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
 5    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
 6    ConfigurableApplicationContext context = null;
 7    configureHeadlessProperty();
 8
 9    // 2、从spring.factories中获取SpringApplicationRunListener对象
10    // 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
11    SpringApplicationRunListeners listeners = getRunListeners(args);
12
13    // 3、发布ApplicationStartingEvent
14    listeners.starting(bootstrapContext, this.mainApplicationClass);
15    try {
16
17        // 4、将run()的参数封装为 DefaultApplicationArguments 对象
18        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
19
20        // 5、准备Environment
21        // 包括操作系统,JVM、ServletContext、properties、yaml等等配置
22        // 会发布一个ApplicationEnvironmentPreparedEvent
23        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
24
25        // 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
26        configureIgnoreBeanInfo(environment);
27        Banner printedBanner = printBanner(environment);
28
29        // 6、根据应用类型创建Spring容器
30        context = createApplicationContext();
31        context.setApplicationStartup(this.applicationStartup);
32
33        // 7、利用ApplicationContextInitializer初始化Spring容器
34        // 8、发布ApplicationContextInitializedEvent
35        // 9、关闭DefaultBootstrapContext
36        // 10、注册primarySources类,就是run方法存入进来的配置类
37        // 11、发布ApplicationPreparedEvent事件
38        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
39
40        // 刷新Spring容器, 会解析配置类、扫描、启动WebServer
41        refreshContext(context);
42
43        // 空方法
44        afterRefresh(context, applicationArguments);
45
46        // 启动时间
47        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
48        if (this.logStartupInfo) {
49            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
50        }
51
52        // 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
53        listeners.started(context, timeTakenToStartup);
54
55        // 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
56        callRunners(context, applicationArguments);
57    } catch (Throwable ex) {
58        // 15、发布ApplicationFailedEvent事件
59        handleRunFailure(context, ex, listeners);
60        throw new IllegalStateException(ex);
61    }
62
63    try {
64        // 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
65        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
66        listeners.ready(context, timeTakenToReady);
67    } catch (Throwable ex) {
68        handleRunFailure(context, ex, null);
69        throw new IllegalStateException(ex);
70    }
71
72    return context;
73}

上面的流程抽丝剥茧之后,主要概括为下面几步:

  1. 首先会去创建一个bootstrapContext对象,有点类似于Spring容器,但是一会儿在下面的prepareConext()这个方法中会关闭这个容器,所以相当于在准备Spring容器之前用的容器。
  2. spring.factories中获取到SpringBoot的一些监听器,这些监听器在下面的启动过程中,将会被赋予不同的事件。比如说解析参数完成、启动完成等事件。
  3. 解析配置参数,prepareConext()在这个方法中。
  4. 创建Spring容器,在这个方法中createApplicationContext(),这个方法会根据当前的应用类型创建不同的容器。
  5. 初始化并刷新前面创建的Spring容器。

PrepareContext()

 1private void prepareContext(
 2    DefaultBootstrapContext bootstrapContext,
 3    ConfigurableApplicationContext context,
 4    ConfigurableEnvironment environment,
 5    SpringApplicationRunListeners listeners,
 6    ApplicationArguments applicationArguments,
 7    Banner printedBanner
 8) {
 9    // 将前面生成的Environment设置到Spring容器中
10    context.setEnvironment(environment);
11
12    // 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
13    postProcessApplicationContext(context);
14
15    // 利用ApplicationContextInitializer初始化Spring容器
16    applyInitializers(context);
17
18    // 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
19    listeners.contextPrepared(context);
20
21    // Spring容器初始化好了,就关闭DefaultBootstrapContext
22    bootstrapContext.close(context);
23    if (this.logStartupInfo) {
24        logStartupInfo(context.getParent() == null);
25        logStartupProfileInfo(context);
26    }
27
28    // 注册一些单例Bean
29    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
30    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
31    if (printedBanner != null) {
32        beanFactory.registerSingleton("springBootBanner", printedBanner);
33    }
34
35    // 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
36    if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
37        ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
38        if (beanFactory instanceof DefaultListableBeanFactory) {
39            ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
40        }
41    }
42    if (this.lazyInitialization) {
43        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
44    }
45
46    // 拿到启动配置类(run方法传进来的类)
47    Set<Object> sources = getAllSources();
48    Assert.notEmpty(sources, "Sources must not be empty");
49
50    // 将启动配置类解析为BeanDefinition注册到Spring容器中
51    load(context, sources.toArray(new Object[0]));
52
53    // 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
54    listeners.contextLoaded(context);
55}

这个方法主要是完成Spring容器的初始化,包括BeanNameGenerator、前面加载好的一些Initializer、注册一些单例bean、将Spring的配置类注册到Spring容器中。这个方法只是错了初始化,还没有进行扫描。



— END —