Spring Boot的启动流程
MainApplication
@SpringBootApplication(scanBasePackages = "cn.hanna.boot")
@Slf4j
public class MainApplication {
public static void main(String[] args) {
// 程序的入口,完成容器的初始化操作获
ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
}
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
// 将主程序类作为主要数据源放入Class数组中
return run(new Class[]{primarySource}, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 1.创建SpringApplication对象 2.调用对象的run方法,完成容器的初始化操作(run方法是核心)
return (new SpringApplication(primarySources)).run(args);
}
new SpringApplication(primarySources)创建SpringApplication实例,进行初始化工作
public SpringApplication(Class<?>... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 1、选择web应用的类型NONE、SERVLET、REACTIVE
// NONE:表示当前的应用即不是一个web应用也不是一个reactive应用,是一个纯后台的应用,不启动内嵌的服务
// SERVLET:当前应用是一个基于servlet API的标准的web应用,需启动内嵌servlet web服务
// Reactive:reactive是spring5当中的新特性,表示是一个响应式的web应用,需启动内嵌的响应式web服务
// 判断依据:跟据类加载器,加载的类来进行判断的
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2、获取spring工厂文件中的实例,实际上就是获取/META-INF/spring.factories中提供的ApplicationContextInitializer类型的实例对象,获取完成后将其赋值给SpringApplication对象的List<ApplicationContextInitializer<?>> initializers属性
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 3、它与2一样,就是获取全部配置文件中ApplicationListener类型的listener集合,将其实例化,作为自己的listeners属性保存
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 设置主程序类,就是我们自定义的带有main方法的类(MainApplication启动类)
this.mainApplicationClass = this.deduceMainApplicationClass();
}
1. WebApplicationType#deduceFromClasspath()
static WebApplicationType deduceFromClasspath() {
// ClassUtils.isPresent()方法,判断指定类名的类是否存在,是否可以进行加载(如果我们导入了,相对应的jar包,就可以加载,如果没有导入就不能加载)
// 如果能够加载DispatcherHandler类,但是无法加载DispatcherServlet和ServletContainer类,就证明是一个响应式web应用
if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
return REACTIVE;
} else {
// SERVLET_INDICATOR_CLASSES = = new String[]{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext"};
String[] var0 = SERVLET_INDICATOR_CLASSES;
int var1 = var0.length;
// 如果不是响应式web应用,并且也不能加载Servlet或ConfigurableWebApplicationContext类,则证明是一个非web应用
for(int var2 = 0; var2 < var1; ++var2) {
String className = var0[var2];
if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
return NONE;
}
}
// 如果能加载到Servlet和ConfigurableWebApplicationContext类,则证明是一个普通的servlet web应用
return SERVLET;
}
}
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
// 可以看到在isPresent()方法中又调用了 forName()
// forName()方法内部主要做的事情就是:获取默认的类加载器,尝试直接加载className对应的类,如果失败,尝试加载该类的内部类,依旧失败,抛出异常
// 如果forName()方法中没有抛出异常,表明加载类成功,返回true
// 如果forName()方法中抛出异常,表明我们没有导入该类相对应的jar包,不打算使用此类作为应用程序的开发,返回false
forName(className, classLoader);
return true;
} catch (IllegalAccessError var3) {
throw new IllegalStateException("Readability mismatch in inheritance hierarchy of class [" + className + "]: " + var3.getMessage(), var3);
} catch (Throwable var4) {
return false;
}
}
总结(应用类型的判断规则):
如果应用程序存在DispatcherHandler并且不存在DispatcherServlet和ServletContainer则为响应式web应用,需加载并启动内嵌的响应式web服务。
如果应用程序不包含Servlet或ConfigurableWebApplicationContext则为普通应用程序。
其他情况则为基于servlet的web应用,需加载并启动内嵌的servlet web服务。
2.SpringApplication#getSpringFactoriesInstances
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
// 记住此处传入的是ApplicationContextInitializer.class
return this.getSpringFactoriesInstances(type, new Class[0]);
}
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 获取SpringApplication的类加载器
ClassLoader classLoader = this.getClassLoader();
// 加载spring.factories文件中的类的全类名集合(此处调用loadFactoryNames()中传入的type是ApplicationContextInitializer.class)
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过反射获取到集合中全类名对应的Class,并创建其实例对象,返回实例对象列表
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 跟据优先级进行排序,返回排序后的实例列表
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
2.1 SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取ApplicationContextInitializer类的全类名(org.springframework.context.ApplicationContextInitializer)
String factoryTypeName = factoryType.getName();
//loadSpringFactories()才正式去加载spring.factories文件,获取文件中的内容,
// getOrDefault()获取类型为ApplicationContextInitializer.class的全类名集合
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 从缓存中获取,首次一定获取不到
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
// 获取META-INF/spring.factories文件所在的全部的URL(绝对路径)
//比如:我们导入的org.springframework.boot:spring-boot:xxx版本号和org.springframework.boot:spring-boot-autoconfigure:xxx版本号的jar包中就存在spring.factories文件
// 总结:实际上就是去获取当前项目中所有META-INF/spring.factories文件的绝对路径
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
// 遍历URL
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
// 加载文件资源到Properties中
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
// 循环properties
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryImplementationName = var9[var11];
// 将spring.factories中类型以及对应的实现类的全类名,放入Map<String,LinkedList<String>>中
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
// 将该类加载器加载的spring.factories文件中的结果集放入缓存,
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
org.springframework.boot:spring-boot:xxx版本
org.springframework.boot:spring-boot-autoconfigure:xxx版本
spring.factories文件
遍历完成后拿到的result
ApplicationContextInitializer类型的全类名集合
ApplicationContextInitializer类型的类实例化对象列表
总结:
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class))
内部的方法:就是去加载当前项目下全部的spring.factories文件,将文件中的key-value放入缓存中,并将读取到的ApplicationContextInitializer类型的类实例化
外部方法:将实例化好的实例对象集合设置为SpringApplication的initializers属性值
看一下,获取到的listeners集合
SpringApplication#run核心方法,完成容器的初始化工作
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
// 保存程序的启动时间
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
//1. 获取运行监听器(重要)
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 2.启动获取到的每一个SpringApplicationRunListener类型的监听器(目前我们之获取到了一个EventPublishingRunListener)
listeners.starting();
Collection exceptionReporters;
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3.准备环境(在准备环境的过程中就会加载类路径下的:配置文件中的信息)
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
// 配置忽略bean信息
this.configureIgnoreBeanInfo(environment);
// 在控制台打印Spring标志
Banner printedBanner = this.printBanner(environment);
// 4.创建应用上下文
context = this.createApplicationContext();
// 获取记录异常的对象
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 5.准备上下文环境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 6.刷新上下文
this.refreshContext(context);
// 7.空方法
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
/**
1.创建应用上下文启动完成事件对象ApplicationStartedEvent
2.发布应用上下文启动完成事件,广播事件,调用监听器相对应的监听方法
**/
listeners.started(context);
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 发布应用准备就绪的事件
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
1.SpringApplication#getRunListeners
private SpringApplicationRunListeners getRunListeners(String[] args) {
Class<?>[] types = new Class[]{SpringApplication.class, String[].class};
//1.getSpringFactoriesInstances()方法我们前面分析过:
// ①去获取spring.factories文件中的指定类型(SpringApplicationRunListener接口)的类的全类名的集合,
// ②在spring.factories中,一般会发现一个EventPublishingRunListener类型的运行监听器
// ③通过反射,实例化EventPublishingRunListener对象
// 2.创建SpringApplicationRunListeners对象,设置它的listeners属性为获取到的运行监听器集合
/**
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList(listeners);
}
**/
return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));
}
1.1 EventPublishingRunListener
public EventPublishingRunListener(SpringApplication application, String[] args) {
// 设置前面创建的SpringApplication对象为其application属性值
this.application = application;
// 参数为空
this.args = args;
// 创建了一个简单应用程序的事件广播器
this.initialMulticaster = new SimpleApplicationEventMulticaster();
for (ApplicationListener<?> listener : application.getListeners()) {
// 添加SpringApplication中的监听器,到广播器中
this.initialMulticaster.addApplicationListener(listener);
}
}
1.2 SpringApplicationRunListeners类的内部结构
class SpringApplicationRunListeners {
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList(listeners);
}
// XXX方法体内部都会遍历每一个监听器,然后调用相应的xxx方法,比如starting()方法的内部,,就会遍历每一个listeners中的每一个listener,调用其starting()方法
void starting() {......}
void environmentPrepared(ConfigurableEnvironment environment){......}
void contextPrepared(ConfigurableApplicationContext context) {......}
void contextLoaded(ConfigurableApplicationContext context) {......}
void started(ConfigurableApplicationContext context) {......}
void running(ConfigurableApplicationContext context){......}
void failed(ConfigurableApplicationContext context, Throwable exception){......}
private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context, Throwable exception){......}
}
2 EventPublishingRunListener#starting开启此事件发布监听器
@Override
public void starting() {
// 2.1.将SpringApplicaiotn对象作为“事件源”,创建了ApplicationStartingEvent事件对象,每个事件对象中都存放着对“源”的引用,表明这些事件是发生在哪个对象上的
// 2.2.对事件进行广播
// 这就是使用了观察者模式
this.initialMulticaster.multicastEvent(new ApplicationStartingEvent(this.application, this.args));
}
// 2.1 创建应用启动事件对象
public ApplicationStartingEvent(SpringApplication application, String[] args) {
super(application, args);
}
public SpringApplicationEvent(SpringApplication application, String[] args) {
super(application);
this.args = args;
}
public ApplicationEvent(Object source) {
super(source);
}
public EventObject(Object source) {
if (source == null)
throw new IllegalArgumentException("null source");
this.source = source;
}
看一下,事件的继承关系图:
2.2 SimpleApplicationEventMulticaster#multicastEvent广播事件
public void multicastEvent(ApplicationEvent event) {
// resolveDefaultEventType()方法,解析事件的类型,将时间类型封装在ResolvableType对象的type属性中
this.multicastEvent(event, this.resolveDefaultEventType(event));
}
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
Executor executor = this.getTaskExecutor();
// 获取能够监听此type类型事件的监听器
Iterator var5 = this.getApplicationListeners(event, type).iterator();
// 遍历获取到的全部监听器,执行相应的监听方法
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
// 广播器中的执行器默认为null
if (executor != null) {
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
// 调用监听器针对此event事件的相应的监听方法
// 针对于不同类型的事件,监听器经过instanceof判断,会调用不同的方法
// 此方法内部用会调用doInvokeListener()方法
this.invokeListener(listener, event);
}
}
}
2.2.1 SimpleApplicationEventMulticaster#doInvokeListener
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 调用当前监听器的onApplicationEvent方法,判断事件类型,调用监听器的相对应方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !this.matchesClassCastMessage(msg, event.getClass())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isTraceEnabled()) {
logger.trace("Non-matching event type for listener: " + listener, var6);
}
}
}
以RestartApplicationListener事件监听器为例
RestartApplicationListener#onApplicationEvent
public void onApplicationEvent(ApplicationEvent event) {
// 判断监听器类型,调用相应方法,进行一些组件的初始化操作
if (event instanceof ApplicationStartingEvent) {
this.onApplicationStartingEvent((ApplicationStartingEvent)event);
}
if (event instanceof ApplicationPreparedEvent) {
this.onApplicationPreparedEvent((ApplicationPreparedEvent)event);
}
if (event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) {
Restarter.getInstance().finish();
}
if (event instanceof ApplicationFailedEvent) {
this.onApplicationFailedEvent((ApplicationFailedEvent)event);
}
}
3.SpringApplication#prepareEnvironment配置环境
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 3.1创建环境对象,依据不同的web应用类型创建不同的环境对象
/**
SERVLET new StandardServletEnvironment();
REACTIVE StandardReactiveWebEnvironment()
NONE new StandardEnvironment()
**/
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 配置环境(环境包括systemEnvironment、systemProperties、命令行参数commandLineArgs,servletContextInitParams、servletConfigInitParams,......)
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
// 创建ApplicationEnvironmentPreparedEvent事件,遍历监听器调用对相应事件的监听方法(与前面SpringApplicationStart事件的流程一样),在ConfigFileApplicationListener监听器中会加载classpath下的配置文件中的内容到PropertySource中
listeners.environmentPrepared(environment);
// 将当前环境绑定到SpringAllication上
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
// 返回环境对象
return environment;
}
看一下配置好的环境对象:
可以看到index=6,已经加载了classpath:application.yml
4.SpringApplication#createApplicationContext
看一下有关applicationContext的继承关系图
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
// 跟据web应用的类型创建相对应类型的应用上下文
switch (this.webApplicationType) {
case SERVLET:
/**
"org.springframework.boot.web.servlet.context.
AnnotationConfigServletWebServerApplicationContext"
**/
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
/**
"org.springframework.boot.web.reactive.context.
AnnotationConfigReactiveWebServerApplicationContext"
**/
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
/**
"org.springframework.context.
annotation.AnnotationConfigApplicationContext"
**/
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
// 实例化应用上下文
/**
读过Spring源码的应该比较好理解,因为他与Spring中创建应用上下文流程基本相同
1.调用AnnotationConfigServletWebServerApplicationContext的空参构造器,在它的空参构造器中最终又会调用父类GenericApplicationContext的空参构造器,在父类的空参构造器中,给当前的上下文new了一个beanFactory对象(DefaultListableBeanFactory)
2.接下来会实例化一个阅读器,实例化阅读器时,又会做以下事情:
注册一些内置的beanDefinition到bean工厂中(记住这个后置处理器【ConfigurationClassPostProcessor】、AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor.....)
3.实例化一个扫描器,除非手动调用.scan其他基本用不到的
**/
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
5.SpringApplication#prepareContext
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
// 设置环境到应用上下文中
context.setEnvironment(environment);
// 进行一些后置处理:给beanFactory的conversionService属性设置值
postProcessApplicationContext(context);
// 遍历前面获取到的放在SpringApplication中的初始化器,调用初始化器的initialize方法,完成一些初始化操作(1.设置应用上下文的id 2、添加了几个beanFactory后置处理器 3.添加了一些对应用上下文的Listener )
applyInitializers(context);
// 创建ApplicationContextInitializedEvent事件,调用监听器相对应的监听方法(与前面相同)
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 注册一个单例bean,到单例池中
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 获主启动类,就是我们编写的带有main方法的类
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// ===加载主启动类(注册主启动类的beanDefinition到bean工厂中)====
/**
1.判断启动类是不是符合条件的isEligible(source),只有以下都为false,才算满足条件,可以注册
type.isAnonymousClass() 判断当前类型是不是匿名类
isGroovyClosure(type) 判断是不是Groovy闭包
hasNoConstructors(type) 判断构造器数组是不是空
**/
load(context, sources.toArray(new Object[0]));
/**
1.将前面获取到的Listener都添加到应用上下文中(Set<ApplicationListener<?>> applicationListeners)
2.如果listener实现了ApplicationContextAware接口,将它的ApplicationContext context属性设置为当前应用上下文
3.遍历添加完成后,创建ApplicationPreparedEvent事件,广播事件(调用监听器的相对应事件的处理方法)
**/
listeners.contextLoaded(context);
}
重点来了
6.SpringApplication#refreshContext(具体细节结合具体问题后续分析)
在此方法内部经过一系列调用最终会调用到ConfigurableApplicationContext#refresh方法
protected void refresh(ConfigurableApplicationContext applicationContext) {
// 调用了我们创建的应用上下文AnnotationConfigServletWebServerApplicationContext的刷新方法,在此方法内部又调用了父类AbstractApplicationContext的refresh()方法,所以说最终还是会与spring完成会合,--->在此方法中会完成,全部非懒加载的bean实例的创建工作
applicationContext.refresh();
}
总结:spring boot应用启动的大体流程
如有问题欢迎指正.....