版本介绍
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
构造方法
我们进入SpringApplication类中,以下是两个构造方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
//存储启动类
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//判断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
//加载所以ApplicationListener.class类型的监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//确定主类
this.mainApplicationClass = deduceMainApplicationClass();
}
构造方法中一些重要的初始化步骤
//判断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//设置监听器
//加载所以ApplicationListener.class类型的监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//确定主类
this.mainApplicationClass = deduceMainApplicationClass();
接下来一个一个具体分析
WebApplicationType.deduceFromClasspath()
简述一下,就是有三种应用类型,分别为
NONE : 该应用不应该作为一个web应用运行,并且不应该启动一个嵌入式web服务。
The application should not run as a web application and should not start an embedded web server.SERVLET:该应用应该运行一个基于servlet的web应用,并且启动一个嵌入式servlet web服务。同步阻塞式 tomcat
The application should run as a servlet-based web application and should start an embedded servlet web server.REACTIVE:该应用应该运行一个reactive web应用,并且应该启动一个嵌入式reactive web服务。异步非阻塞式
The application should run as a reactive web application and should start an embedded reactive web server.反应式web应用,实际就是web客户端和后端会有一个管道,后端将改变数据不断的推送到客户端,而不是传统的WEB应用将后端数据获得后发送给客户端显示,这样前端就被阻塞必须等待后端数据处理完成才能显示,反应式编程是依据观察者和订阅者模式来实现的,这样缩短了客户端的响应时间,单因为客户端的不确定性,可能前端很容易就被后端推送的数据压垮,所以有了被压(back pressure)概念,直到Jdk 9才对reactive有了支持,spring 5的flux就是一个reactive框架。
static WebApplicationType deduceFromClasspath() {
//如果当前包下存在WEBFLUX_INDICATOR_CLASS,并且不存在WEBMVC_INDICATOR_CLASS和JERSEY_INDICATOR_CLASS,则为反应式类型
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
//若SERVLET_INDICATOR_CLASSES的类都不存在,则NONE
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
setInitializers();
ApplicationContextInitializer是用于在刷新容器之前初始化ConfigurableApplicationContext的回调接口,我们也可以实现ApplicationContextInitializer接口来在容器刷新之前做一些我们的初始化逻辑。
- 通过SPI机制加载所有类型为ApplicationContextInitializer的组件,并创建对应的实例
- 把创建出来的实例设置到initializers属性中
//核心方法
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
//创建类加载器
ClassLoader classLoader = getClassLoader();
// Use names and ensure unique to protect against duplicates
//加载工厂名字,存入集合set
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//创建spring工厂实例,并存入列表
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
//排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//获取工厂类型名
String factoryTypeName = factoryType.getName();
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文件
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 = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
//继续遍历配置
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];
//将类和相关信息存入result
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
private <T> List<T> createSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes,
ClassLoader classLoader, Object[] args, Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
//获取反射
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass.getDeclaredConstructor(parameterTypes);
//创建实例
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
//一些相关代码
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
//不重要
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
//不重要
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
//这个接口继承了ConfigurableApplicationContext接口
public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
void initialize(C var1);
}
//返回loadSpringFactories 如果这个key的value不为null或者有这个key,返回该键值对,否则返回默认值
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
setListeners();
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
核心方法与上方一致getSpringFactoriesInstances()
- 通过SPI机制加载所有类型为ApplicationListener的组件,并创建对应的实例
- 把创建出来的实例设置到listeners属性中
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
deduceMainApplicationClass();
跟着当前方法的调用栈往上找,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
//跟着当前方法的调用栈往上找,哪一层调用栈上有main方法,方法对应的类就是主配置类,就返回这个类。
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
run方法
核心方法
- 创建时间监控器,并且计时
- 配置awt信息
- 获取监控,并且开始监控
- 创建并配置环境
- 若spring.beaninfo.ignore配置了,则加载配置
- 打印banner
- 创建上下文
- 初始化异常报告器
- 初始化ioc
- 刷新上下文
- 后置方法预留
- 停止计时
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
public ConfigurableApplicationContext run(String... args) {
//创建时间性能监控器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
//创建空的IOC容器
ConfigurableApplicationContext context = null;
//创建一组异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置与awt相关的信息
configureHeadlessProperty();
//获取SpringApplicationRunListeners
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
try {//创建参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
//创建并配置环境
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
//如果配置了spring.beaninfo.ignore,则配置环境
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建上下文对象
context = createApplicationContext();
//初始化异常报告器
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//初始化IOC容器
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;
}
SpringApplicationRunListener
public interface SpringApplicationRunListener { //第一次调用run时调用,早期初始化 default void starting() { } //在环境准备好后调用,但在ApplicationContext创建之前调用 default void environmentPrepared(ConfigurableEnvironment environment) { } //ApplicationContext之后,加载之前调用 default void contextPrepared(ConfigurableApplicationContext context) { } //加载之后,刷新之前 default void contextLoaded(ConfigurableApplicationContext context) { } //上下文已刷新,应用程序已启动,但CommandLineRunners 和 ApplicationRunner尚未调用 default void started(ConfigurableApplicationContext context) { } //在运行方法完成之前立即调用,当应用程序上下文具有已刷新所有 CommandLineRunners和ApplicationRunners已被调用 default void running(ConfigurableApplicationContext context) { } //在运行应用程序时发生故障时调用。 default void failed(ConfigurableApplicationContext context, Throwable exception) { }
prepareEnvironment
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
listeners.environmentPrepared(environment);
//环境与应用绑定
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
创建上下文
//这里根据不同的环境创建不同的IOC容器,但是创建的都是基于Annotation的ApplicationContext.
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
初始化IOC
- 将创建好的Environment设置到IOC容器中
- 执行IOC容器的后置处理
- 执行Initializer
- 执行listeners#contextPrepared回调方法
- 创建两个组件用来在控制台打印Banner
- 加载和注册主启动类
- 回调listeners#contextLoaded方法
//初始化IOC
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
//将环境配置到上下文中
context.setEnvironment(environment);
//后置处理
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
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
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
后置处理
- 如果
beanNameGenerator
不为null,则将其注册为单例Bean。这意味着在应用程序上下文中只有一个beanNameGenerator
实例。- 如果
resourceLoader
不为null,则根据context
的类型设置资源加载器。如果context
是GenericApplicationContext
类型,则直接设置其资源加载器;如果context
是DefaultResourceLoader
类型,则设置其类加载器。- 如果
addConversionService
为true,则将ApplicationConversionService
的共享实例设置为应用程序上下文的转换服务。这样可以方便地在应用程序中使用不同类型的数据转换。
protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
if (this.beanNameGenerator != null) {
context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR,
this.beanNameGenerator);
}
if (this.resourceLoader != null) {
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).setResourceLoader(this.resourceLoader);
}
if (context instanceof DefaultResourceLoader) {
((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader());
}
}
if (this.addConversionService) {
context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
}
}
应用初始化器
- 使用
getInitializers()
方法获取所有的初始化器,并遍历它们。- 对于每个初始化器,使用
GenericTypeResolver.resolveTypeArgument()
方法解析出所需的类型(requiredType)。- 使用
Assert.isInstanceOf()
方法检查context
是否是所需类型的实例。如果不是,将抛出异常并显示错误信息:"Unable to call initializer."。- 如果
context
是所需类型的实例,则调用初始化器的initialize()
方法,并将context
作为参数传递。
protected void applyInitializers(ConfigurableApplicationContext context) {
for (ApplicationContextInitializer initializer : getInitializers()) {
Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(),
ApplicationContextInitializer.class);
Assert.isInstanceOf(requiredType, context, "Unable to call initializer.");
initializer.initialize(context);
}
}
刷新上下文
private void refreshContext(ConfigurableApplicationContext context) {
refresh((ApplicationContext) context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
后置刷新(预留方法)
protected void afterRefresh(ConfigurableApplicationContext context, ApplicationArguments args) {
}
一些其他的相关方法
//配置awt java可视化组件
private void configureHeadlessProperty() {
System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,
System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));
}
private void configureIgnoreBeanInfo(ConfigurableEnvironment environment) {
if (System.getProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME) == null) {
Boolean ignore = environment.getProperty("spring.beaninfo.ignore", Boolean.class, Boolean.TRUE);
System.setProperty(CachedIntrospectionResults.IGNORE_BEANINFO_PROPERTY_NAME, ignore.toString());
}
}
参考博客
springboot源码分析——启动流程分析_springboot启动流程源码分析-CSDN博客