文章目录
一、前言
在日常开发中springboot是我们应用非常普遍的开发框架。springboot可以帮助我们快速的开发一个后端应用,我们只需引入依赖,添加简单的配置就能实现对其他框架的整合。他的奥妙就在于它各种各样的starter。
1.1 starter的作用
SpringBoot这些starter的作用就是根据我们配置,给我们初始化一些整合其他框架时需要初始化的一些bean,并加载到spring容器中。这样就减少了在SSM时代那些繁琐的是xml配置。
1.2 环境信息
依赖 | 版本 |
---|---|
springboot | 2.4.10 |
二、原理
2.1 概述
那么springboot是如何实现通过,通过引入一些jar包的依赖就将这些jar包中定义的spring的bean加载到spring容器中的呢?答案就是spring的SPI机制。
2.2 什么是spi
SPI(Service Provider Interface)就是服务提供接口,它是实现服务解耦,插件自由插拔的一直机制。简单来说,就是通过一些类加载器,去加载classpath下指定目录文件,文件中定义的有需要加载的类的全权限定名,然后这些类会被识别并加载。平时开发中数据库连接驱动的加载和dubbo中都用到了SPI技术。正是使用了SPI技术,才实现了插件和服务的解耦和可插拔。
2.3 Spring的SPI
Spring的SPI使用由SpringFactoriesLoader.loadFactoryNames方法实现的,
它会加载所有依赖的jar包下classPath下META-INF/spring.factories文件,然后将解析properties文件,找到指定名称的配置后返回。
源码如下:
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();
return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* spring.factories文件的格式为:key=value1,value2,value3
* 从所有的jar包中找到META-INF/spring.factories文件
* 然后从文件中解析出key=factoryClass类名称的所有value值
*/
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 {
// 取得资源文件的URL
Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);
// 遍历所有的URL
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<