Springboot启动过程分析
首先从一个入口程序开始
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
| 1@SpringBootApplication
2public class SpringLearnApplication {
3
4 public static void main(String[] args) {
5 System.out.println(SpringLearnApplication.class.getClassLoader());
6 //从这个run方法开始
7 SpringApplication.run(SpringLearnApplication.class, args);
8 }
9
10}
11
12public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
13 //这个方法分为两步,首先我们来看创建SpringApplication对象
14 return new SpringApplication(primarySources).run(args);
15 }
16
17public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
18 this.resourceLoader = resourceLoader;
19 ...
20 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
21 //推断应用类型,servlet还是reactor and so on
22 this.webApplicationType = WebApplicationType.deduceFromClasspath();
23 //实例化ApplicationContextInitializer接口的所有实现类
24 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
25 //实例化ApplicationListener接口的所有实现类
26 setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
27 //推断主应用程序的fully qualified class name
28 this.mainApplicationClass = deduceMainApplicationClass();
29 }
30 //deduceMainApplicationClass()方法的核心代码:
31StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
32 for (StackTraceElement stackTraceElement : stackTrace) {
33 if ("main".equals(stackTraceElement.getMethodName())) {
34 return Class.forName(stackTraceElement.getClassName());
35 }
36 }
37
38 |
加载spring factory
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
| 1 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
2 //用于存储从"META-INF/spring.factories"文件中加载的类的fully qualified class name
3 MultiValueMap<String, String> result = cache.get(classLoader);//第一次为空
4 if (result != null) {
5 return result;
6 }
7
8 try {
9 Enumeration<URL> urls = (classLoader != null ?
10 classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
11 ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
12 result = new LinkedMultiValueMap<>();
13 while (urls.hasMoreElements()) {
14 URL url = urls.nextElement();
15 //分别获取到beans,springboot,autoconfigture下面的META-INF/spring.factories文件路径
16 UrlResource resource = new UrlResource(url);
17 Properties properties = PropertiesLoaderUtils.loadProperties(resource);
18 for (Map.Entry<?, ?> entry : properties.entrySet()) {
19 String factoryTypeName = ((String) entry.getKey()).trim();
20 for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
21 //把分别获从beans,springboot,autoconfigture下面获的META-INF/spring.factories文件中获取到的所有fully qualified class name添加到容器中,待后面的实例化
22 result.add(factoryTypeName, factoryImplementationName.trim());
23 }
24 }
25 }
26 cache.put(classLoader, result);
27 return result;
28 }
29 ...
30 }
31
32 |
第二步,启动程序
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
| 1public ConfigurableApplicationContext run(String... args) {
2 //该秒表用于计算程序执行的时间
3 StopWatch stopWatch = new StopWatch();
4 stopWatch.start();
5 ConfigurableApplicationContext context = null;
6 Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
7 //表示是一个运行于服务器的程序,没有键盘
8 configureHeadlessProperty();
9 //获取到所有实现SpringApplicationRunListener该接口的实现类的集合
10 SpringApplicationRunListeners listeners = getRunListeners(args);
11 //调用监听器starting()方法启动对应用的监听
12 listeners.starting();
13 try {
14 ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
15 //根据应用程序类型创建对应的环境,此处创建的是StandardServletEnvironment环境
16 ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
17 configureIgnoreBeanInfo(environment);
18 //打印Banner信息,默认为控制台输出spring标志及版本
19 Banner printedBanner = printBanner(environment);
20 //通过实例化AnnotationConfigServletWebServerApplicationContext创建上下文环境
21 context = createApplicationContext();
22 //实例化实现了SpringBootExceptionReporter该接口的所有类
23 exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
24 new Class[] { ConfigurableApplicationContext.class }, context);
25 prepareContext(context, environment, listeners, applicationArguments, printedBanner);
26 refreshContext(context);
27 afterRefresh(context, applicationArguments);
28 stopWatch.stop();
29 if (this.logStartupInfo) {
30 new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
31 }
32 listeners.started(context);
33 callRunners(context, applicationArguments);
34 }
35 catch (Throwable ex) {
36 handleRunFailure(context, ex, exceptionReporters, listeners);
37 throw new IllegalStateException(ex);
38 }
39
40 try {
41 listeners.running(context);
42 }
43 catch (Throwable ex) {
44 handleRunFailure(context, ex, exceptionReporters, null);
45 throw new IllegalStateException(ex);
46 }
47 return context;
48 }
49
50 |