文章目录
一、main()方法调用
1. main()方法
@SpringBootApplication
public class SpringBootHelloworldQuickApplication {
public static void main(String[] args) {
// 先从main()方法入手,传入当前类
SpringApplication.run(SpringBootHelloworldQuickApplication.class, args);
}
}
2. SpringApplication.run()方法
run()方法会调用如下的方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,String[] args) {
// primarySources是当前启动类
return new SpringApplication(primarySources).run(args);
}
该方法分两步走:
- 创建
SpringApplication
对象 - 调用
springApplicaiton
对象的run()
方法,其中args
可以传递启动时需要的个性化参数
二、创建SpringApplication对象
1. SpringApplication()构造方法
该方法最终会调用SpringApplication
的构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
//保存主配置类SpringBootHelloworldQuickApplication.class
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断当前是否一个web应用,主要是判断是普通web应用、响应式web应用、非web应用
this.webApplicationType = deduceWebApplicationType();
//从类路径下找到META‐INF/spring.factories,找到其中配置的所有ApplicationContextInitializer,然后保存起来,重要!!!!
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//从类路径下找到ETA‐INF/spring.factories,找到其中配置的所有ApplicationListener,然后保存起来,重要!!!!
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = deduceMainApplicationClass();
}
Debug
调试获取到的ApplicationContextInitializer
和ApplicationListener
,启动时候会用到
2. deduceWebApplicationType()
private WebApplicationType deduceWebApplicationType() {
/**
** 如果程序中有org.springframework.web.reactive.DispatcherHandler这个类且没有
** org.springframework.web.servlet.DispatcherServlet和
** org.glassfish.jersey.servlet.ServletContainer 这两类那么就是REACTIVE
**/
if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_WEB_ENVIRONMENT_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//SERVLET_INDICATOR_CLASSES = { "javax.servlet.Servlet",
// "org.springframework.web.context.ConfigurableWebApplicationContext" }
// 如果程序中这两个类有一个不存在就是NONE
for (String className : WEB_ENVIRONMENT_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 如果上面都不是那就是SERVLET
return WebApplicationType.SERVLET;
}
3. getSpringFactoriesInstances()
该方法内部,最终会调用loadSpringFactories()
方法;
loadSpringFactories()
方法内部会加载META-INF/spring.factories
文件,这里加载的文件不仅包含项目中的,还包换我们项目环境所依赖的jar
包中的META-INF/spring.factories
文件。
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//获取当前ClassLoader下的所有包含META-INF/spring.factories文件的URL路
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
//遍历所有的包含META-INF/spring.factories文件URL集合
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//转换为Properties对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//遍历META-INF/spring.factories文件中的所有属性
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//如果一个接口希望配置多个实现类,可以使用','进行分割,将当前Key对应的值转换为List
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
//添加到返回对象中
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
} catch (IOException ex) {
// 抛异常代码略
}
}
4. 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) {
// Swallow and continue
}
return null;
}
三、调用springApplicaiton对象的run()方法
调用run()方法代码入口
new SpringApplication(primarySources).run(args);
1. run()
public ConfigurableApplicationContext run(String... args) {
// 创建一个用来方便的记录程序的运行时间的对象
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 设置系统属性java.awt.headless,默认为true,运行在没有显示器和键盘的环境。
configureHeadlessProperty();
// 获取SpringApplicationRunListeners,从类路径下META‐INF/spring.factories
SpringApplicationRunListeners listeners = getRunListeners(args);
// 回调所有的获取SpringApplicationRunListener.starting()方法
listeners.starting();
try {
//封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 创建并配置当前SpringBoot应用将要使用的Environment,
// 创建环境完成后回调SpringApplicationRunListener.environmentPrepared();
// 表示环境准备完成
ConfigurableEnvironment environment = prepareEnvironment(listeners,applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
// 创建ApplicationContext,该过程和Spring创建容器启动过程一样
context = createApplicationContext();
// 使用SpringFactoriesLoader在应用的classpath中查找并加载所有可用的SpringBootExceptionReporter
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 准备应用上下文[整个容器的创建与启动以及bean的注入功能]
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
// 刷新应用上下文[是实现spring-boot-starter-*的自动化配置的关键]
refreshContext(context);
// 从ioc容器中获取所有的ApplicationRunner和CommandLineRunner进行回调,
// ApplicationRunner先回调,CommandLineRunner再回调
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
// 启动应用程序中的所有监听器(创建SpringApplication对象过程中有获取到)
listeners.started(context);
// 调用应用程序中所有CommandLineRunner.class和ApplicationRunner.class的实现类的run方法
callRunners(context, applicationArguments);
} catch (Throwable ex) {
// 抛异常代码略
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
//整个SpringBoot应用启动完成以后返回启动的ioc容器;
return context;
}
protected ConfigurableApplicationContext createApplicationContext() {
return this.applicationContextFactory.create(this.webApplicationType);
}
// 根据webApplicationType类型来创建Spring容器
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
// 如果是SERVLET创建"org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext"
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
// 如果是REACTIVE创建"org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext"
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
// 默认是创建org.springframework.context.annotation.AnnotationConfigApplicationContext
return new AnnotationConfigApplicationContext();
}
} catch (Exception ex) {
// 抛异常代码略
}
};
四、总结
从上面源码可以看出,SpringApplication
的run
方法主要执行以下逻辑:
- 创建一个用来方便的记录程序的运行时间的对象
- 设置系统属性
java.awt.headless
,默认为true
,运行在没有显示器和键盘的环境 - 加载并启动一系列
SpringApplicationRunListener
对象 - 创建并配置当前
SpringBoot
应用将要使用的Environment
- 如果
SpringApplication
的showBanner
属性被设置为true
,则打印banner
- 创建
ApplicationContext
- 使用
SpringFactoriesLoader
在应用的classpath
中查找并加载所有可用的SpringBootExceptionReporter
- 准备应用上下文[整个容器的创建与启动以及
bean
的注入功能] - 刷新应用上下文[是实现
spring-boot-starter-\*
的自动化配置的关键] - 调用
ApplicationContext
的refresh()
方法,完成IoC
容器可用的最后一步操作 - 启动应用程序中的所有监听器
- 调用应用程序中所有
CommandLineRunner.class
和ApplicationRunner.class
的实现类的run
方法 - 发布事件
五、补充
重要的事件回调机制,配置在META-INF/spring.factories
ApplicationContextInitializer
SpringApplicationRunListener
只需要放在IOC
容器中
ApplicationRunner
CommandLineRunner