Spring Boot的准备
首先,我们看一下在启动项中 SpringApplication为run的启动做了哪些准备:
SpringApplication方法中调用了其它的方法
我们首先来看一看 this.webApplicationType = WebApplicationType.deduceFromClasspath();
public enum WebApplicationType {
/**
* The application should not run as a web application and should not start an
* embedded web server.
*/
NONE,
/**
* The application should run as a servlet-based web application and should start an
* embedded servlet web server.
*/
SERVLET,
/**
* The application should run as a reactive web application and should start an
* embedded reactive web server.
*/
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";
private static final String SERVLET_APPLICATION_CONTEXT_CLASS = "org.springframework.web.context.WebApplicationContext";
private static final String REACTIVE_APPLICATION_CONTEXT_CLASS = "org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext";
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;
}
static WebApplicationType deduceFromApplicationContext(Class<?> applicationContextClass) {
if (isAssignable(SERVLET_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.SERVLET;
}
if (isAssignable(REACTIVE_APPLICATION_CONTEXT_CLASS, applicationContextClass)) {
return WebApplicationType.REACTIVE;
}
return WebApplicationType.NONE;
}
private static boolean isAssignable(String target, Class<?> type) {
try {
return ClassUtils.resolveClassName(target, null).isAssignableFrom(type);
}
catch (Throwable ex) {
return false;
}
}
}
上面的便是WebApplicationType的源码,可以看出调用的deduceFromClasspath()方法的内容。
大体来说的意思就是:
如果存在 org.springframework.web.reactive.DispatcherHandler 类
并且不存在org.springframework.web.servlet.DispatcherServlet类和org.glassfish.jersey.servlet.ServletContainer类时,则决定使用 REACTIVE 模式;
如果javax.servlet.Servlet类和org.springframework.web.context.ConfigurableWebApplicationContext类有任意一个不存在,则决定使用NONE模式;
否则的话就采用SERVLET模式。
由于spring-boot-start-web依赖引入了tomcat和spring-web,所以会自动选择SERVLET模式。
接着,通过getSpringFactoriesInstances()方法来加载ApplicationContextInitializer
和加载ApplicationListener
我们来看一看getSpringFactoriesInstances()方法
在此方法中又使用SpringFactoriesLoader类来将类路径下的所有resources/META-INF/spring.factory文件的key作为BootStrapper接口的值都加载出来作为该接口的实现类列表。
接着调用createSpringFactoriesInstances()方法
此方法通过实现类来实现,通过反射完成。
搞清楚getSpringFactoriesInstances()方法之后,那么对于
加载ApplicationContextInitializer,便就是获取ApplicationContextInitializer接口实现类并创建实例对象,赋值给initializers成员变量;
加载ApplicationListener,便就是获取ApplicationListener接口实现类并创建实例对象,赋值给listeners成员变量。
最后调用deduceMainApplicationClass()
通过手动创建一个RuntimeException对象,并获取当前方法的堆栈,一直向上,直到推到main方法所在类并返回。
Spring Boot运行阶段
/**
* Run the Spring application, creating and refreshing a new
* {@link ApplicationContext}.
* @param args the application arguments (usually passed from a Java main method)
* @return a running {@link ApplicationContext}
*/
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
我们来看一下run()方法的源码
StopWatch是一个小组件,这里是计算运行时间的一个小工具
首先调用configureHeadlessProperty()方法设置headless模式
通过配置java.awt.headless属性(这里默认为true)为true
接着获取所有的监听器
从spring.facotries内找到所有SpringApplicationRunListener接口的实现来创建实例对象,将这些对象封装到SpringApplicationRunListeners对象中;
发送Spring Boot开始事件
环境配置
首先将run()方法中的参数封装成ApplicationArguments;
再进行初始化和配置Environment对象;
最后设置IngoreBeanInfoProperty的属性,决定是否跳过spring对BeanInfo类的扫描。
打印banner
首先判断当前banner模式是否开启
如果是Log模式,则使用logger进行打印banner
否则采用标准输出流输出banner
如果是logger模式则会将banner转成string,在用logger的info级别进行输出
如果是输出流模式,则直接调用banner的printBanner方法进行输出
创建ApplicationContext
上面我们说过,webApplicationType的模式为SERVLET,所以我们创建的ApplicationContext对象就是AnnotationConfigServletWebServerApplicationContext
初始化配置ApplicationContext
refreshContext
AfterRefresh
context启动事件 & Runner调用 & 运行
1.调用所有SpringApplicationRunListener的started()方法
2.回调ApplicationRunner和CommandLineRunner
3.调用所有SpringApplicationRunListener的running()方法