SpringBoot启动之SpringApplication初始化详解
作者:my_sky_
SpringApplication初始化
当启动SpringBoot应用后,经过两步,会进入到new SpringApplication(primarySources).run(args)。
1、primarySources参数
primarySources参数实际为Spring Boot应用上下文的Configuration Class,在后面扫描配置类时起作用。
2、SpringApplication初始化
SpringApplication对象的初始化
具体操作包括:
- 首先初始化资源加载器,默认为null;断言判断主要资源类不能为null,否者报错。
- 然后将主资源类primarySources存储到SpringApplication对象Set类型的primarySources属性中。
- 推断当前 WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE;默认是SERVLET。
- 加载Spring应用上下文初始化器:从"META-INF/spring.factories"文件中读取ApplicationContextInitializer类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序后,赋值到SpringApplication的List类型的initializers属性上,一共7个。
- 加载Spring应用事件监听器:从"META-INF/spring.factories"文件中读取ApplicationListener类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序后,赋值到SpringApplication的List类型的listeners属性上,一共11个。
- 推断主入口应用类:通过当前调用栈的解析,获取Main方法所在类,并赋值给SpringApplication的mainApplicationClass属性。
1)推断Web应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
这里推断的Web应用类型是默认的,我们还可以手动的再通过setWebApplicatioinType(WebApplicationType)方法进行调整。这里通过检查当前ClassLoader下基准Class的存在性来推断Web应用类型。
public enum WebApplicationType { NONE, SERVLET, REACTIVE; private static final String[] SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }; private static final String WEBMVC_INDICATOR_CLASS = "org.springframework.web.servlet.DispatcherServlet"; private static final String WEBFLUX_INDICATOR_CLASS = "org.springframework.web.reactive.DispatcherHandler"; private static final String JERSEY_INDICATOR_CLASS = "org.glassfish.jersey.servlet.ServletContainer"; .... static WebApplicationType deduceFromClasspath() { // 1. 如果`DispatcherHandler`存在,并且`DispatcherServlet`和`ServletContainer`不存在时,Web应用类型为REACTIVE; if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null) && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) { return WebApplicationType.REACTIVE; } // 2. 如果`Servlet`和`ConfigurableWebApplicationContext`不存在,则当前应用为非Web引应用,即NONE。 for (String className : SERVLET_INDICATOR_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } // 3.当Spring WebFlux和Spring Web MVC同时存在时,Web应用依旧是SERVLET。 return WebApplicationType.SERVLET; } .... }
WEB 应用类型,一共有三种:NONE,SERVLET,REACTIVE。
deduceFromClasspath()方法利用ClassUtils.isPresent(String, ClassLoader)方法依次判断reactive.DispatcherHandler、ConfigurableWebApplicationContext、Servlet、servlet.DispatcherServlet的存在性组合情况,从而判断Web 引用类型,具体逻辑如下:
- 如果DispatcherHandler存在,并且DispatcherServlet和ServletContainer不存在时,即:Spring Boot仅依赖WebFlux时,Web应用类型为REACTIVE;
- 如果Servlet和ConfigurableWebApplicationContext不存在,则当前应用为非Web应用,即NONE。因为这两个API是Spring Web MVC必须的依赖。
- 当Spring WebFlux和Spring Web MVC同时存在时,Web应用类型依旧是SERVLET。
2)加载Spring应用上下文初始化器ApplicationContextInitializer
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
这个过程包括两个动作:
1. getSpringFactoriesInstances(ApplicationContextInitializer.class)从"META-INF/spring.factories"文件中读取ApplicationContextInitializer类的实例名称集合,然后进行Set去重、利用反射实例化对象,最后按照Order排序。
2. setInitializers(Collection)将Collection赋值到SpringApplication的List类型的initializers属性上,一共7个。
3)加载Spring事件应用监听器ApplicationListener
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
这个过程和加载Spring应用上下文初始化器ApplicationContextInitializer一样(区别在于这里不再直接从spring.factories文件中获取内容,而是走cache(MultiValueMap<String, String>)缓存)。
4)推断应用引导类
this.mainApplicationClass = deduceMainApplicationClass();
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) { } return null; }
该方法根据当前线程执行栈来判断其栈中哪个类包含main方法,然后将找到的类名通过反射返回Class对象。
至此,在SpringApplication构造过程中,SpringApplication属性primarySources、webApplicationType、initializers、listeners 和 mainApplicationClass都被初始化了。
到此这篇关于SpringBoot启动之SpringApplication初始化详解的文章就介绍到这了,更多相关SpringApplication初始化内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!