spring-boot通过下面一段简单的代码就能启动运行起来,那么它是如何做的呢,本篇文章就是来梳理new SpringApplication(ApplicationConfig.class).run(args);的执行过程。主要探索的主题有两个:
@SpringBootApplication
public class Chapter03Application {
public static void main(String[] args) {
new SpringApplication(ApplicationConfig.class).run(args);
}
}
首先是new SpringApplication(ApplicationConfig.class)做了什么
public SpringApplication(Class<?>... primarySources) {
// 调用另一个构造器
this(null, primarySources);
}
new SpringApplication(ApplicationConfig.class)是做了如下操作
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader; // null
// 主配置类,即通常是@SpringBootApplication注解的类,本例中指的是Chapter03Application,
// 也可以不是只能标注在main方法类上,也可以其他标注了@SpringBootApplication的配置类,通常不这么做
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 这里推断使用哪种应用服务器类型,两种SERVLET和REACTIVE,WebApplicationType枚举类中定义,稍后看deduceFromClasspath()方法里做了什么。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 这里是从spring.factories文件中读取ApplicationContextInitializer类型的类,并实例化。从名称上来看他们时上下文初始化器,用来控制上下文初始化的
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 这里是从spring.factories文件中读取ApplicationListener类型的类,并实例化
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 通过运行时的栈信息获取main方法入口所在的类
this.mainApplicationClass = deduceMainApplicationClass();
}
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() {
// 这里会根据classpath下是否具有相应的类型来确定应用启动的方式,
// 当引入webflux的依赖,并排除webmvc依赖时将采用REACTIVE的方式启动应用,
// 否则的话将采用SERVLET的方式,这是一般常用的方式,即使用内嵌的tomcat启动应用
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;
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {
});
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(
// 加载出spring.factories中的org.springframework.context.ApplicationContextInitializer的值
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 实例化这些类型,将会在prepareContext()中准备应用上下文时使用这些初始化器,
// 稍后会在run方法中看到
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
private Class<?> deduceMainApplicationClass() {
try {
// 从运行方法栈中推断出main方法类,也即是在debugger过程中看到的调用栈
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;
}
即下面这个调用栈信息
以上在new SpringApplication()主要做了这么几件事儿,1. 设置主配置,2. 推断出应用引入了那种web依赖,时webmvc还是webflux,判断采用那种web server类型,3. 读取spring.factories文件,加载配置的ApplicationContextInitializer和ApplicationListener.
run(args)
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 定义应用上下文和异常报告
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置java.awt.headless,Headless模式是系统的一种配置模式。在系统可能缺少显示
// 设备、键盘或鼠标这些外设的情况下可以使用该模式。
configureHeadlessProperty();
// 从spring.factories中加载SpringApplicationRunListener,用于在应用启动过程中发布启动各阶段的事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发布开始事件
listeners.starting();
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 创建运行环境,这里将会发布org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent事件,
// 然后org.springframework.boot.context.config.ConfigFileApplicationListener监听到该事件
// 后会加载application.properties,稍后会分析这部分代码
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 创建应用上下文
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] {
ConfigurableApplicationContext.class }, context);
// 准备应用上下文,对于web应用,这里的应用上下文是一个ReactiveWebServerApplicationContext(RREACTIVE)
// 或是AnnotationConfigServletWebServerApplicationContext(SERVLET)
// 他们都实现了onRefresh()方法,在该方法中将创建Web Server
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新应用上下文,即启动Spring IOC, 将会调用onRefresh()
refreshContext(context);
// 空方法
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
// 将会执行实现了ApplicationRunner或CommandLineRunner接口的对象的run方法
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;
}
加载application.properties是在ConfigFileApplicationListener监听到ApplicationEnvironmentPreparedEvent事件后做的
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
// 处理事件
onApplicationEnvironmentPreparedEvent(
(ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
private void onApplicationEnvironmentPreparedEvent(
ApplicationEnvironmentPreparedEvent event) {
// 从spring.factories中加载org.springframework.boot.env.EnvironmentPostProcessor值对应的类型
List<EnvironmentPostProcessor> postProcessors = loadPostProcessors();
// ConfigFileApplicationListener本身也是一个EnvironmentPostProcessor
postProcessors.add(this);
// 根据Order排序
AnnotationAwareOrderComparator.sort(postProcessors);
// 执行EnvironmentPostProcessor
for (EnvironmentPostProcessor postProcessor : postProcessors) {
postProcessor.postProcessEnvironment(event.getEnvironment(),
event.getSpringApplication());
}
}
// ConfigFileApplicationListener的postProcessEnvironment()
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
// 添加属性配置到environment
addPropertySources(environment, application.getResourceLoader());
}
protected void addPropertySources(ConfigurableEnvironment environment,
ResourceLoader resourceLoader) {
RandomValuePropertySource.addToEnvironment(environment);
// 加载外部化属性配置application.properties
new Loader(environment, resourceLoader).load();
}
private void load(Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
// 加载application.properties的位置
getSearchLocations()
.forEach((location) -> {
boolean isFolder = location.endsWith("/");
Set<String> names = isFolder ? getSearchNames() : NO_SEARCH_NAMES;
names.forEach(
(name) -> load(location, name, profile, filterFactory, consumer));
});
}
private Set<String> getSearchLocations() {
// CONFIG_LOCATION_PROPERTY = "spring.config.location" 如果通过命令行指定该参数,那么将使用该参数指定的配置,否则将到默认位置读取
if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
return getSearchLocations(CONFIG_LOCATION_PROPERTY);
}
Set<String> locations = getSearchLocations(
CONFIG_ADDITIONAL_LOCATION_PROPERTY);
// DEFAULT_SEARCH_LOCATIONS = "classpath:/,classpath:/config/,file:./,file:./config/",默认位置
locations.addAll(
asResolvedSet(ConfigFileApplicationListener.this.searchLocations,
DEFAULT_SEARCH_LOCATIONS));
return locations;
}
从上面来看,配置文件的优先级顺序是:
创建Web Server是在启动spring ioc过程中调用onRefresh()来创建
// AbstractApplicationContext#refresh()
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// 这里ReactiveWebServerApplicationContext(RREACTIVE)
// 和AnnotationConfigServletWebServerApplicationContext(SERVLET)两个子
// 类均重写了onRefresh()方法. 对于AnnotationConfigServletWebServerApplicationContext,
//是它的父类ServletWebServerApplicationContext重写的onRefresh()
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// ServletWebServerApplicationContext与ReactiveWebServerApplicationContext
// 复写了该方法,并且在复写方法中启动Web Server
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
// ServletWebServerApplicationContext#onRefresh()
@Override
protected void onRefresh() {
super.onRefresh();
try {
// 创建web server
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
// 创建 web server
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// getSelfInitializer() 创建并初始化webServer, 之后将在
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context",
ex);
}
}
initPropertySources();
}
// ServletWebServerApplicationContext#finishRefresh()
@Override
protected void finishRefresh() {
// 调用父类finishRefresh()
super.finishRefresh();
// 启动web serevr
WebServer webServer = startWebServer();
if (webServer != null) {
// 发布事件
publishEvent(new ServletWebServerInitializedEvent(webServer, this));
}
}
private WebServer startWebServer() {
WebServer webServer = this.webServer;
if (webServer != null) {
// 启动web server 对于本示例中启动的是tomcat,
// @see org.springframework.boot.web.embedded.tomcat.TomcatWebServer#start()
webServer.start();
}
return webServer;
}
以上内容通过源码对spring-boot启动流程做了大致的梳理,其中仍有很多细节需要探索