SpringBoot 启动类 SpringApplication 一 构造器方法
SpringApplication
@SpringBootApplication
@ComponentScan(basePackages = { "com.example.*" })
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args); // Spirngboot程序入口
}
}
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 首先用主类(DemoApplication.class)为参数调用构造器方法,之后执行run()方法
return new SpringApplication(primarySources).run(args);
}
构造器方法
第一个参数ResourceLoader
是资源加载器,它定义获取资源的策略。它加载各种来源的资源,比如文件资源(file:/var/local/a.txt
),类路径资源(classpath: application.yml
),URL资源(URL:https://www.baidu.com
),并且统一封装为Resource
对象。
Resource
对象是对各种资源的抽象,调用Resource
对象不用考虑底层实现。
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = 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)); # 初始化BootstrapRegistry
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContext
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); #
this.mainApplicationClass = deduceMainApplicationClass();
}
判断服务类型
WebApplicationType
是枚举类,有3种类型
NONE
表示非web应用,不需要web服务器SERVLET
表示基于SERVLET的WEB应用,需要SERVLET服务器REACTIVE
表示响应式WEB应用,需要响应式web服务器
spring-mvc
默认使用的Tomcat
属于第二种,因此springApplication.webApplicationType = SERVLET
,后续在refresh()
方法里会启动Tomcat
。
ClassUtils.isPresent
调用Class.forName()
方法查询类路径上是否存在某个类。
public enum WebApplicationType {
NONE,
SERVLET,
REACTIVE;
private static final String[] SERVLET_INDICATOR_CLASSES = { "jakarta.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)) {
// 如果JVM无法加载WEBMVC_INDICATOR_CLASS类和JERSEY_INDICATOR_CLASS
// 但是JVM可以加载WEBFLUX_INDICATOR_CLASS,则为响应式web
return WebApplicationType.REACTIVE;
}
// 只要没有SERVLET_INDICATOR_CLASSES 中任意一个类,都属于非WEB服务
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 同时包含SERVLET_INDICATOR_CLASSES的所有类,属于SERVELT web服务
return WebApplicationType.SERVLET;
}
}
getSpringFactoriesInstances
private <T> List<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, null);
}
private <T> List<T> getSpringFactoriesInstances(Class<T> type, ArgumentResolver argumentResolver) {
return SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader()).load(type, argumentResolver);
}
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}
getSpringFactoriesInstances
方法分2步.第一步加载工厂类SpringFactoriesLoader.forDefaultResourceLocation(getClassLoader())
,DefaultResourceLocation
就是"META-INF/spring.factories"
.第二步实例化工厂类load(type, argumentResolver)
。
加载工厂类
public class SpringFactoriesLoader {
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
static final Map<ClassLoader, Map<String, SpringFactoriesLoader>> cache = new ConcurrentReferenceHashMap<>();
// 加载"META-INF/spring.factories"工厂类的类加载器
private final ClassLoader classLoader;
private final Map<String, List<String>> factories;
// 默认加载"META-INF/spring.factories"中的类
public static SpringFactoriesLoader forDefaultResourceLocation(@Nullable ClassLoader classLoader) {
return forResourceLocation(FACTORIES_RESOURCE_LOCATION, classLoader);
}
}
SpringFactoriesLoader
是spring.factories
工厂类文件的加载器。forDefaultResourceLocation
静态方法表示默认从每个jar包的"META-INF/spring.factories"
文件加载类。
forResourceLocation
静态方法实现加载工厂类并且保存在SpringFactoriesLoader
类对象的静态变量cache
中,每次调用getSpringFactoriesInstances
方法就不用重复加载"META-INF/spring.factories"
。
public static SpringFactoriesLoader forResourceLocation(String resourceLocation, @Nullable ClassLoader classLoader) {
Assert.hasText(resourceLocation, "'resourceLocation' must not be empty");
// 获得类加载器,用于加载类,是AppClassLoader系统类加载器
ClassLoader resourceClassLoader = (classLoader != null ? classLoader :
SpringFactoriesLoader.class.getClassLoader());
// loaders是当前类加载器所加载的资源键值对,key是资源名称,是`META-INF/spring.factories`,value是SpringFactoriesLoader对象
// cache是类的静态变量,全局唯一
Map<String, SpringFactoriesLoader> loaders = cache.computeIfAbsent(
resourceClassLoader, key -> new ConcurrentReferenceHashMap<>());
return loaders.computeIfAbsent(resourceLocation, key ->
new SpringFactoriesLoader(classLoader, loadFactoriesResource(resourceClassLoader, resourceLocation)));
}
loadFactoriesResource
实现用AppClassLoader
加载"META-INF/spring.factories"
。结合下图分析源码。
protected static Map<String, List<String>> loadFactoriesResource(ClassLoader classLoader, String resourceLocation) {
Map<String, List<String>> result = new LinkedHashMap<>();
try {
// Enumeration不是枚举类,是迭代器类
Enumeration<URL> urls = classLoader.getResources(resourceLocation);
while (urls.hasMoreElements()) {
// resource是`META-INF/spring.factories`文件的路径
UrlResource resource = new UrlResource(urls.nextElement());
// Properties类定义:`class Properties extends Hashtable<Object,Object>`
// Properties类是线程安全的属性类,用键值对表示属性
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
properties.forEach((name, value) -> {
// 将键值对的值用逗号分隔开,并且将其放入result的value
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) value);
List<String> implementations = result.computeIfAbsent(((String) name).trim(),
key -> new ArrayList<>(factoryImplementationNames.length));
Arrays.stream(factoryImplementationNames).map(String::trim).forEach(implementations::add);
});
}
// 表示将result的value变成不可变list
result.replaceAll(SpringFactoriesLoader::toDistinctUnmodifiableList);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" + resourceLocation + "]", ex);
}
// 表示将result整个map变成不可变map
return Collections.unmodifiableMap(result);
}
图中的spirng.factories
是本项目自定义的。Springboot
会加载所有包的spring.factories
。比如:spring-boot-autoconfigure-3.3.5.jar!/META-INF/spring.factories, spring-data-redis-3.3.5.jar!/META-INF/spring.factories
。
实例化工厂类
SpringApplication
构造器方法里setInitializers
方法的参数ApplicationContextInitializer.class
用于实例化该接口的实现类,即factoryType
。
load
方法首先根据factoryType
参数获取实现类的全限定类名,之后实例化。
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver) {
return load(factoryType, argumentResolver, null);
}
public <T> List<T> load(Class<T> factoryType, @Nullable ArgumentResolver argumentResolver,
@Nullable FailureHandler failureHandler) {
Assert.notNull(factoryType, "'factoryType' must not be null");
// loadFactoryNames方法就是获取cache中保存的`factoryType`的实现类全限定类名
List<String> implementationNames = loadFactoryNames(factoryType);
logger.trace(LogMessage.format("Loaded [%s] names: %s", factoryType.getName(), implementationNames));
List<T> result = new ArrayList<>(implementationNames.size());
FailureHandler failureHandlerToUse = (failureHandler != null) ? failureHandler : THROWING_FAILURE_HANDLER;
for (String implementationName : implementationNames) {
// 实例化对象
T factory = instantiateFactory(implementationName, factoryType, argumentResolver, failureHandlerToUse);
if (factory != null) {
result.add(factory);
}
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
protected <T> T instantiateFactory(String implementationName, Class<T> type,
@Nullable ArgumentResolver argumentResolver, FailureHandler failureHandler) {
try {
// 在JVM中加载implementationName实现类
Class<?> factoryImplementationClass = ClassUtils.forName(implementationName, this.classLoader);
// 校验实现类是否是type的子类
Assert.isTrue(type.isAssignableFrom(factoryImplementationClass), () ->
"Class [%s] is not assignable to factory type [%s]".formatted(implementationName, type.getName()));
// FactoryInstantiator<T>获得`factoryImplementationClass`类的构造器对象`Constructor<T>`
FactoryInstantiator<T> factoryInstantiator = FactoryInstantiator.forClass(factoryImplementationClass);
// 调用构造器对象的`constructor.newInstance(args)`方法生成对象。
return factoryInstantiator.instantiate(argumentResolver);
}
catch (Throwable ex) {
failureHandler.handleFailure(type, implementationName, ex);
return null;
}
}
初始化
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class)); # 初始化BootstrapRegistry
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); # 初始化ApplicationContext
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
上下文
this.bootstrapRegistryInitializers
加载META-INF/spring.factories
中BootstrapRegistryInitializer.class
的实现类,用于初始化DefaultBootstrapContext
上下文对象。而setInitializers
加载ApplicationContextInitializer.class
的实现类,用于初始化ApplicationContext
上下文对象。
这两个上下文,前者是引导上下文,在IOC容器启动之前保存预先加载的类,IOC容器启动之后会销毁引导上下文。一般springboot项目用不到引导上下文
,springcloud
中有用到。
后者就是IOC容器。SpringBoot
默认加载图中7个初始化器。
监听器
setListeners
方法加载ApplicationListener.class
的实现类,用于监听启动SpringApplication
定义的事件。
具体流程是,自定义SpringApplicationRunListener
实现类,定义监听的事件。
public class MyListener implements ApplicationListener<ApplicationEvent> {
// 当出现事件(`ApplicationEvent`的实现类),`onApplicationEvent`就会被执行。
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("事件: " + event);
}
@Override
public boolean supportsAsyncExecution() {
return ApplicationListener.super.supportsAsyncExecution();
}
}
在META-INF/spring.factories
加一org.springframework.context.ApplicationListener=com.example.demo.MyListener
。
启动项目,加载的实现类中就包含自定义的实现类。
deduceMainApplicationClass
推导主类。源码表示:在(栈帧StackWalker
对象)中找到第一个名称为main
的方法,该方法所在的类就是主类。
private Class<?> deduceMainApplicationClass() {
return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE)
.walk(this::findMainClass)
.orElse(null);
}
private Optional<Class<?>> findMainClass(Stream<StackFrame> stack) {
return stack.filter((frame) -> Objects.equals(frame.getMethodName(), "main"))
.findFirst()
.map(StackWalker.StackFrame::getDeclaringClass);
}