从0开始深入理解Spring-启动类构造
引言:
从本篇开始,打算深入理解Spring执行过程及原理,个人理解极有可能有偏差,评论区欢迎指正错误,下面开始进入正片内容。
ps: springboot版本使用2.4.8
Springboot项目启动时,是通过main方法中编写启动类的形式启动的,按照下面格式编写启动类
@SpringBootApplication
public class SpringFrameStudyApplication {
public static void main(String[] args) {
SpringApplication.run(SpringFrameStudyApplication.class);
}
}
其核心为SpringApplication.run(SpringFrameStudyApplication.class)
方法,下面深入探讨该方法的执行
SpringApplication.java位于org.springframework.boot包下,是Springboot项目启动的核心类。点击run()方法。进入该方法
这里run方法不做过多解析,下篇会详解run方法的具体执行过程
SpringApplication.java
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
return run(new Class<?>[] { primarySource }, args);
}
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 在这里创建了SpringApplication对象,并执行run方法。执行完毕后返回配置好的上下文对象ConfigurableApplicationContext
return new SpringApplication(primarySources).run(args);
}
// 构造方法
public SpringApplication(Class<?>... primarySources) {
this(null, primarySources);
}
// SpringApplication核心构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
// 资源加载器: 如果按照上述方法进行构造时,为null
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// webApplicationType属性用于判断运行容器: 为Servlet还是Reactive。Reactive为响应式的架构: SpringWebFlex
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 这里重点介绍下。从SpringFactories中获得Boot的注册器及初始化器等相关信息并将springfatory中的信息初始化至缓存中的
this.bootstrapRegistryInitializers = getBootstrapRegistryInitializersFromSpringFactories();
// 设置Application上下文初始化器 相关属性(赋值操作)
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 设置应用监听初始化器 相关属性(赋值操作)
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 赋值 jvm线程栈中 main方法栈所在类
this.mainApplicationClass = deduceMainApplicationClass();
}
这里避免类代码过长过重,拆分两部分进行代码讲解。下面这一部分介绍getBootstrapRegistryInitializersFromSpringFactories()
是如何从MATE-INFO/spring.factory中获取相关属性
SpringApplication.java
private List<BootstrapRegistryInitializer> getBootstrapRegistryInitializersFromSpringFactories() {
ArrayList<BootstrapRegistryInitializer> initializers = new ArrayList<>();
// 核心方法,获得Bootstrapper类有关的SpringFactory实例
getSpringFactoriesInstances(Bootstrapper.class).stream()
.map((bootstrapper) -> ((BootstrapRegistryInitializer) bootstrapper::initialize))
.forEach(initializers::add);
// 获得BootstrapRegistryInitializer类有关的SpringFactory实例
initializers.addAll(getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
return initializers;
}
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {
this.initializers = new ArrayList<>(initializers);
}
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) {
this.listeners = new ArrayList<>(listeners);
}
/**
* 根据类型获得SpringFacotries实例
* @param type
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
return getSpringFactoriesInstances(type, new Class<?>[] {});
}
/**
* 根据类型获得SpringFacotries实例
* @param type 类类型
* @param parameterTypes 参数类型
* @param args 构造方法的参数
*/
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
// 这里getClassLoader()为内部的一个方法
ClassLoader classLoader = getClassLoader();
// 根据传入的类获得SpringFactories中的限定名称
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 根据名称集合创建实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
// 按照order进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
public ClassLoader getClassLoader() {
if (this.resourceLoader != null) {
// 正常默认启动时,resourceLoader为空
return this.resourceLoader.getClassLoader();
}
// 默认返回当前主线程
return ClassUtils.getDefaultClassLoader();
}
SpringFactoriesLoader.java
// 获取spring.factories中的所在位置
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
// 加载所有springfactories的实现类并 根据factoryTypeName获得该实现类的名称
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 {
// 类加载器中获得spring.factories的所在url地址
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 加载resource资源
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
String[] factoryImplementationNames =
StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
for (String factoryImplementationName : factoryImplementationNames) {
// 添加实现类名称
result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>())
.add(factoryImplementationName.trim());
}
}
}
// 将其列表去重并修改为不可修改的集合
result.replaceAll((factoryType, implementations) -> implementations.stream().distinct()
.collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));
// 加入缓存中 key为classLoader
cache.put(classLoader, result);
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
return result;
}
构造SpringApplication实例的过程实际上是完成了spring.factories文件的扫描,并将扫描好的factories配置放入缓存中(key: className, values: List names)。按照需要去获取设置 上下文、监听等 实现类。