源码版本:SpringBoot2.7.18
@SpringBootApplication
public class BootSourceApplication {
public static void main(String[] args) {
SpringApplication.run(BootSourceApplication.class, args);
}
}
用过SpringBoot框架的朋友们对上面的代码一定十分熟悉。今天我们就一起来看一下为啥简简单单几行代码就能为我启动一个Spring应用。
进入SpringApplication#run(Class<?> primarySource, String... args)
public class SpringApplication {
//.......
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);
}
}
-
进入 run(Class<?> primarySource, String... args)
方法后立即调用另一个run(Class<?>[] primarySources, String[] args)
方法 -
run(Class<?>[] primarySources, String[] args)
方法主要做了下面两件事 -
创建了一个 SpringApplication
实例 -
执行 run(String... args)
方法
创建SpringApplication
实例
public class 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();
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
//..........
}
我们来看一下SpringApplication
构造方法中做了哪些操作
-
设置 resourceLoader
,这里传进来的是null
-
设置 primarySources
主配置源,这里传进来的是BootSourceApplication.class
-
推断应用类型 webApplicationType
,可能是非Web
、Servlet-Web
、Reactive-Web
-
设置 bootstrapRegistryInitializers
,获取BootstrapRegistryInitializer
的实现类 -
设置 initializers
,获取ApplicationContextInitializer
的实现类 -
设置 listeners
,获取ApplicationListener
的实现类 -
推断启动类 mainApplicationClass
推断应用类型
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
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";
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;
}
}
通过反射判断应用的类路径下是否存在相应的字节码,来判断是哪种应用类型
NONE:非Web应用 SERVLET:Servlet Web应用 REACTIVE:REACTIVE Web应用
getSpringFactoriesInstances(Class<T> type)
public class SpringApplication {
//...........
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//..................
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
//.....................
}
//..........
}
大家看一下上面的代码,在寻找
BootstrapRegistryInitializer
、ApplicationContextInitializer
和ApplicationListener
时,都是通过getSpringFactoriesInstances(Class<T> type)
方法去寻找的,下面我们就研究一下这个方法的作用
public class SpringApplication {
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 = getClassLoader();
// Use names and ensure unique to protect against duplicates
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(type, classLoader)
最终是去重后的names -
createSpringFactoriesInstances
创建实例 -
AnnotationAwareOrderComparator.sort
将实例排序并返回
SpringFactoriesLoader.loadFactoryNames(type, classLoader)
最终是读取类路径下所有的META-INF/spring.factories
文件,最终将其转换成一个Map<String,List<String>>
public final class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
//....
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
//....
return result;
}
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = cache.get(classLoader);
if (result != null) {
return result;
}
result = new HashMap<>();
try {
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
for (Map.Entry<?, ?> entry : properties.entrySet()) {
//......
}
}
cache.put(classLoader, result);
}catch(){}
return result;
}
}
createSpringFactoriesInstances
方法就是通过全限定名创建实例
AnnotationAwareOrderComparator.sort
是对实例进行排序,具体实现看AnnotationAwareOrderComparator
和其父类OrderComparator
。这里就不看了
SpringBoot可以在
META-INF/spring.factories
定义实现类,并且可以自动加载并实例化。这被称为SPI机制
推断启动类
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;
}
推断启动类的方式也是让XiXi眼前一亮,通过获取运行的堆栈信息,来找到main方法的那个栈帧信息,取main方法所在的类作为启动类
run(String... args)
方法
public class SpringApplication {
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context);
afterRefresh(context, applicationArguments);
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
listeners.started(context, timeTakenToStartup);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
}
-
创建 DefaultBootstrapContext
-
获取所有的 SpringApplicationRunListeners
-
执行 SpringApplicationRunListeners#starting()
-
创建 ApplicationArguments
:DefaultApplicationArguments
类 -
准备 ConfigurableEnvironment
:prepareEnvironment
方法 -
打印Banner -
创建应用上下文: createApplicationContext
-
准备应用上下文: prepareContext
-
刷新应用上下文: refreshContext
-
应用上下文刷新后操作: afterRefresh
-
执行 SpringApplicationRunListeners#started()
-
执行Runner: ApplicationRunner
和CommandLineRunner
-
执行 SpringApplicationRunListeners#ready()
-
返回应用上下文
若以上步骤中出现异常情况,均通过
handleRunFailure
进行处理
涉及SpringApplicationRunListeners的流程
这里我将
SpringApplicationRunListeners
在run方法中的执行流程梳理一下
class SpringApplicationRunListeners {
private final List<SpringApplicationRunListener> listeners;
}
SpringApplicationRunListeners
包含一个SpringApplicationRunListener
列表,SpringApplicationRunListeners
的方法调用,实际是遍历每一个SpringApplicationRunListener
进行执行。 这里XiXi感觉是用到了一点设计模式,但是又没有找到完全能对应上的设计模式。我这里暂且认为是组合模式
涉及应用上下文的流程
这里我将应用上下文在run方法中的流程抽出来整理一下
主要涉及应用上下的创建、准备、刷新。每个动作后面又会涉及到一堆逻辑。这里就不展开了
总结
好的兄弟们,SpringBoot
的整个启动代码我们就分析到这里。有些具体的点后面会依依写文章分析。谢谢大家的阅读。Taco Tuesday!!!
原文始发于微信公众号(溪溪技术笔记):SpringBoot源码阅读-启动流程
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/207084.html