那么启动过程分为两步分析
- 创建SpringApplication实例过程
- 实例run()方法执行过程
入口在springboot启动类
@SpringBootApplication
public class CloudApplication {
public static void main(String[] args) {
//调用SpringApplication类静态run方法
SpringApplication.run(CloudApplication.class, args);
}
}
跟踪代码
public static ConfigurableApplicationContext run(Class<?> primarySource,
String... args) {
//调用当前类另一个静态方法
return run(new Class<?>[] { primarySource }, args);
}
跟踪到另一个静态方法
public static ConfigurableApplicationContext run(Class<?>[] primarySources,
String[] args) {
//先创建实例,调用实例run()方法
return new SpringApplication(primarySources).run(args);
}
一.创建实例
创建实例主要做了四件事:
- 推断应用类型
- 类加载并实例化所有的ApplicationContextInitializer
- 类加载并实例化所有的ApplicationListener
- 推断springboot启动类是哪个
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//资源加载器
this.resourceLoader = resourceLoader;
//断言资源加载器非空
Assert.notNull(primarySources, "PrimarySources must not be null");
//设置源primarySources
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
//推断应用类型
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//设置初始化器
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
//设置监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//推断启动类
this.mainApplicationClass = deduceMainApplicationClass();
}
1.1推断应用类型
其实就是根据,类路径下是否存在以下关键类判断返回哪个枚举值。后面的过程会根据应用类型创建对应类型的容器 。WebApplicationType是一个枚举类,有三个枚举值
- NONE:按照非web应用运行并且不创建内置的web服务器
- SERVLET:按照一个基于servlet的web应用运行,并且创建内置的web容器
- REACTIVE:按照一个反应式的web应用运行,并肩创建一个反应式的web容器
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;
}
ClassUtils.isPresent 方法,是判断类路径下是否存在类,如果存在就类加载并返回true,否则返回false。总结一下类型推断的过程其实就是:看类路径下是否存在应用对应的关键类,根据是否存在返回对应的枚举值。后面根据枚举值来创建对应类型的应用。
1.2类加载并实例化所有的ApplicationContextInitializer
调用getSpringFactoriesInstances(ApplicationContextInitializer.class)方法 ,作用是返回所有ApplicationContextInitializer类型的扩展类实例,主要分两步进行:
- 先获取所有要加载类的全限定名
- 根据全限定名实例化对象
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
//获取类全限定名
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
//根据类名创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
然后跟踪获取全限定名的方法SpringFactoriesLoader.loadFactoryNames,这个方法是springboot加载扩展类实现扩展机制的关键,主要分两步:
- 先获取当前类加载器对用的所有组件类型的所有类
- 根据组件类型从第一步返回的map的获取对应组件类型的所有类
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
最终调用方法如下
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//从缓存中获取,ApplicationClassLoader对应的扩展类是否加载过了
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
//如果加载过了直接返回
return result;
}
try {
//遍历所有Jar包 如果存在META-INF/spring.factories文件,就将文件加载为资源
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
//创建一个复合结构map<String,List<String>> key扩展类型全限定名,如:org.springframework.context.ApplicationContextInitializer,value为所有扩展实现类的全限定名集合
result = new LinkedMultiValueMap<>();
//遍历每个文件对应的资源
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
//将资源转化为属性对象
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//每个文件中有多组扩展,每一个扩展的属性值可能都有多个,遍历每一个扩展类型
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryClassName = ((String) entry.getKey()).trim();
//遍历该扩展类型对应的属性值
for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
- 先从缓存中获取对应类加载器的全部要加载的类
- 若果获取不到,就去读取jar包中的META-INF/spring.factories文件
- 对每个文件遍历,将结果放到result中一个LinkedMultiValueMap,LinkedMultiValueMap的key为加载类型的全限定名,value为要加载类的集合
- 将result放入到cache中,key为类加载器 ,value为result
-
类加载器对应的全部类型组件的全部类的类名集合
image.png
每次调用SpringFactoriesLoader.loadFactoryNames时先从缓存cache成员变量中获取,也就是说在应用启动过程中除第一次调用此方法都是从缓存返回的。
1.3设置监听器
原理与设置初始化器相同
1.4推断启动类
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) {
}
return null;
}
二.run方法执行过程
public ConfigurableApplicationContext run(String... args) {
//创建计时器,用于记录一段程序的执行时间
StopWatch stopWatch = new StopWatch();
//开始计时
stopWatch.start();
//spring上下文对象
ConfigurableApplicationContext context = null;
//异常报告器
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
//配置系统属性
configureHeadlessProperty();
//使用扩展方式,获取SpringApplication对象run方法专用的监听器 (其实不是监听器,而是发布事件用的)
SpringApplicationRunListeners listeners = getRunListeners(args);
//发布开始事件
listeners.starting();
try {
//获取参数,args为启动类main方法的入参
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
//准备spring应用运行时环境
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
configureIgnoreBeanInfo(environment);
//打印banner
Banner printedBanner = printBanner(environment);
//创建spring应用上下文对象
context = createApplicationContext();
//获取异常报告器
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
//准备spring应用上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
//刷新spring应用上下文
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;
}
2.1获取SpringApplicationRunListeners
跟前边获取ApplicationContextInitializer,ApplicationListener的过程是一样的 ,通过SpringFactoriesLoader.loadFactoryNames(type, classLoader)方法获取类名,然后实例化 。
主要说下这个类的作用:在springboot应用启动过程中发布事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
跟踪下starting()方法
public void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
此处其实只有一个SpringApplicationRunListener,实现类是EventPublishingRunListener,继续跟EventPublishingRunListener源码
public void starting() {
this.initialMulticaster.multicastEvent(
new ApplicationStartingEvent(this.application, this.args));
}
initialMulticaster是spring事件派发器 ,在EventPublishingRunListener构造方法调用时被创建
public EventPublishingRunListener(SpringApplication application, String[] args) {
//获取到SpringApplication
this.application = application;
this.args = args;
//创建事件派发器
this.initialMulticaster = new SimpleApplicationEventMulticaster();
//从SpringApplication获取所有注册在上面的监听器,将这些监听器注册到事件派发器上
for (ApplicationListener<?> listener : application.getListeners()) {
this.initialMulticaster.addApplicationListener(listener);
}
}
前边,SpringApplication创建实例的时候通过扩展方式 从META-INF/spring.factories中获取的那些监听器就在这里使用。
EventPublishingRunListener其实是委托事件派发器去派发给监听器事件。
2.2准备spring应用运行时环境
private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
//创建应用环境对象
ConfigurableEnvironment environment = getOrCreateEnvironment();
//配置应用环境对象
configureEnvironment(environment, applicationArguments.getSourceArgs());
//发布环境准备就绪事件
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader())
.convertEnvironmentIfNecessary(environment, deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
跟踪创建环境对象代码 ,根据之前推断的应用类型,创建的是StandardServletEnvironment类型的环境对象
private ConfigurableEnvironment getOrCreateEnvironment() {
if (this.environment != null) {
return this.environment;
}
switch (this.webApplicationType) {
case SERVLET:
return new StandardServletEnvironment();
case REACTIVE:
return new StandardReactiveWebEnvironment();
default:
return new StandardEnvironment();
}
}
跟踪配置环境对象代码
protected void configureEnvironment(ConfigurableEnvironment environment,
String[] args) {
if (this.addConversionService) {
ConversionService conversionService = ApplicationConversionService
.getSharedInstance();
environment.setConversionService(
(ConfigurableConversionService) conversionService);
}
//配置property
configurePropertySources(environment, args);
//配置profiles
configureProfiles(environment, args);
}
spring环境对象分两部分 profiles和property
profiles是一组BeanDefination的逻辑区分 ,当一个profiles处于active状态,处于同一profiles的BeanDefination才会被注册到容器中
property包含: 属性文件(properties files),JVM系统属性,系统环境变量,JNDI,servlet上下文参数,临时属性对象等。
2.3创建spring应用上下文对象
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);
}
根据之前的应用类型推断 ,创建一个AnnotationConfigServletWebServerApplicationContext类型的上下文对象
2.4
private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
//给上下文对象关联环境对象
context.setEnvironment(environment);
//注册bean生成器和资源加载器
postProcessApplicationContext(context);
//回调前面从扩展中获取的初始化器的初始化方法
applyInitializers(context);
//发布容器上下文准备完毕事件
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 添加两个特殊单例bean
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
//源只有当前移动类一个
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//发布上下文加载完成事件
listeners.contextLoaded(context);
}
2.5刷新容器
调用父类的refresh()方法 ,也就是一般spring容器非boot框架启动时都要执行的那个refresh方法。太复杂了,就不展开了 。
最后
其实启动过程没什么新奇的东西,事件监听啊bean定义,bean注册等等,主要还是spring容器启动那一套 ,增加了一些springboot用到的特殊组件,另外一个需要注意的地方就是扩展方式从 META-INF/spring.factories中获取扩展类,springboot自动配置等很多功能都是这种实现方式
网友评论