SpringBoot 启动过程源码探究

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

启动类

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

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

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

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

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

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

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

    //		SpringApplication.run(SpringBootApplicationG.class, args);
}

这样写扩展性更强一点。

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;

    Assert.notNull(primarySources, "PrimarySources must not be null");

    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));

    // 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();

    // 2. 从spring.factories中获取BootstrapRegistryInitializer对象
    this.bootstrapRegistryInitializers = new ArrayList<>(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));

    // 3. 从spring.factories中获取ApplicationContextInitializer对象
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));

    // 4. 从spring.factories中获取ApplicationListener对象
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

    // 5. 推测出Main类(main()方法所在的类)
    this.mainApplicationClass = deduceMainApplicationClass();
}

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

推断应用类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler";
private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet";
private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };


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;
}
  1. 如果ClassPath中既没有DispatcherServlet,没有DispatcherHandler类,那么就是REACTIVE应用;
  2. 接下来,如果ClassPath中不存在ServletConfigurableWebApplicationContext类,则应用类型为NONE;
  3. 否则就是SERVLET应用

加载初始化器

1
2
3
4
5
6
7
8
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;
}

ApplicationContextInitializer对应的spring.factories中的value:

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

加载监听器

记在一些默认的监听器:

1
2
3
4
5
6
7
8
9
# 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,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener

其中的EnvironmentPostProcessorApplicationListener比较重要。

推断Main类

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

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

Run()方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();

    // 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();

    // 2、从spring.factories中获取SpringApplicationRunListener对象
    // 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
    SpringApplicationRunListeners listeners = getRunListeners(args);

    // 3、发布ApplicationStartingEvent
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {

        // 4、将run()的参数封装为 DefaultApplicationArguments 对象
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);

        // 5、准备Environment
        // 包括操作系统,JVM、ServletContext、properties、yaml等等配置
        // 会发布一个ApplicationEnvironmentPreparedEvent
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);

        // 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);

        // 6、根据应用类型创建Spring容器
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);

        // 7、利用ApplicationContextInitializer初始化Spring容器
        // 8、发布ApplicationContextInitializedEvent
        // 9、关闭DefaultBootstrapContext
        // 10、注册primarySources类,就是run方法存入进来的配置类
        // 11、发布ApplicationPreparedEvent事件
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);

        // 刷新Spring容器, 会解析配置类、扫描、启动WebServer
        refreshContext(context);

        // 空方法
        afterRefresh(context, applicationArguments);

        // 启动时间
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }

        // 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
        listeners.started(context, timeTakenToStartup);

        // 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
        callRunners(context, applicationArguments);
    } catch (Throwable ex) {
        // 15、发布ApplicationFailedEvent事件
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }

    try {
        // 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    } catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }

    return context;
}

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

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

PrepareContext()

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
private void prepareContext(
    DefaultBootstrapContext bootstrapContext,
    ConfigurableApplicationContext context,
    ConfigurableEnvironment environment,
    SpringApplicationRunListeners listeners,
    ApplicationArguments applicationArguments,
    Banner printedBanner
) {
    // 将前面生成的Environment设置到Spring容器中
    context.setEnvironment(environment);

    // 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
    postProcessApplicationContext(context);

    // 利用ApplicationContextInitializer初始化Spring容器
    applyInitializers(context);

    // 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
    listeners.contextPrepared(context);

    // Spring容器初始化好了,就关闭DefaultBootstrapContext
    bootstrapContext.close(context);
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }

    // 注册一些单例Bean
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }

    // 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
    if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
        ((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
        if (beanFactory instanceof DefaultListableBeanFactory) {
            ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
        }
    }
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }

    // 拿到启动配置类(run方法传进来的类)
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");

    // 将启动配置类解析为BeanDefinition注册到Spring容器中
    load(context, sources.toArray(new Object[0]));

    // 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
    listeners.contextLoaded(context);
}

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




相关内容

0%