SpringBoot 启动过程源码探究
接下来我们从源码的角度来看下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}
- 如果ClassPath中既没有
DispatcherServlet,没有DispatcherHandler类,那么就是REACTIVE应用; - 接下来,如果ClassPath中不存在
Servlet和ConfigurableWebApplicationContext类,则应用类型为NONE; - 否则就是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}
上面的流程抽丝剥茧之后,主要概括为下面几步:
- 首先会去创建一个bootstrapContext对象,有点类似于Spring容器,但是一会儿在下面的
prepareConext()这个方法中会关闭这个容器,所以相当于在准备Spring容器之前用的容器。 - 从
spring.factories中获取到SpringBoot的一些监听器,这些监听器在下面的启动过程中,将会被赋予不同的事件。比如说解析参数完成、启动完成等事件。 - 解析配置参数,
prepareConext()在这个方法中。 - 创建Spring容器,在这个方法中
createApplicationContext(),这个方法会根据当前的应用类型创建不同的容器。 - 初始化并刷新前面创建的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 —