SpringFactoriesLoader.loadFactoryNames 方法的关键源码:
// spring.factories 文件的格式为:key=value1,value2,value3// 遍历所有 META-INF/spring.factories 文件// 解析文件,获得 key=factoryClass 的类名称public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 尝试获取缓存,如果缓存中有数据 , 直接返回MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 获取资源文件路径Enumeration<URL> urls = (classLoader != null ?classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));result = new LinkedMultiValueMap<>();// 遍历所有路径while (urls.hasMoreElements()) {URL url = urls.nextElement();UrlResource resource = new UrlResource(url);// 解析文件,得到对应的一组 PropertiesProperties properties = PropertiesLoaderUtils.loadProperties(resource);// 遍历解析出的 properties,组装数据for (Map.Entry<?, ?> entry : properties.entrySet()) {String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {result.add(factoryTypeName, factoryImplementationName.trim());}}}cache.put(classLoader, result);return result;}catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}}归纳上面的方法,主要作了这些事:
加载所有 META-INF/spring.factories 文件 , 加载过程有 SpringFactoriesLoader 负责 。
- 在 CLASSPATH 中搜寻所有 META-INF/spring.factories 配置文件 。
- 然后,解析 spring.factories 文件,获取指定自动装配类的全限定名 。
以 spring-boot-starter-web 的 jar 包为例,查看其 maven pom,可以看到,它依赖于 spring-boot-starter,所有 Spring Boot 官方 starter 包都会依赖于这个 jar 包 。而 spring-boot-starter 又依赖于 spring-boot-autoconfigure,Spring Boot 的自动装配秘密,就在于这个 jar 包 。
从 spring-boot-autoconfigure 包的结构来看,它有一个 META-INF/spring.factories ,显然利用了 Spring Boot SPI,来自动装配其中的配置类 。
文章插图
下图是 spring-boot-autoconfigure 的 META-INF/spring.factories 文件的部分内容,可以看到其中注册了一长串会被自动加载的 AutoConfiguration 类 。
文章插图
以 RedisAutoConfiguration 为例,这个配置类中,会根据 @ConditionalXXX 中的条件去决定是否实例化对应的 Bean , 实例化 Bean 所依赖的重要参数则通过 RedisProperties 传入 。
文章插图
RedisProperties 中维护了 Redis 连接所需要的关键属性,只要在 yml 或 properties 配置文件中,指定 spring.redis 开头的属性,都会被自动装载到 RedisProperties 实例中 。
文章插图
通过以上分析 , 已经一步步解读出 Spring Boot 自动装载的原理 。
五、SPI 应用案例之 DubboDubbo 并未使用 Java SPI , 而是自己封装了一套新的 SPI 机制 。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容形式如下:
optimusPrime = org.apache.spi.OptimusPrimebumblebee = org.apache.spi.Bumblebee与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样可以按需加载指定的实现类 。Dubbo SPI 除了支持按需加载接口实现类,还增加了 IOC 和 AOP 等特性 。
5.1 ExtensionLoader 入口Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,可以加载指定的实现类 。
ExtensionLoader 的 getExtension 方法是其入口方法,其源码如下:
public T getExtension(String name) {if (name == null || name.length() == 0)throw new IllegalArgumentException("Extension name == null");if ("true".equals(name)) {// 获取默认的拓展实现类return getDefaultExtension();}// Holder,顾名思义 , 用于持有目标对象Holder<Object> holder = cachedInstances.get(name);if (holder == null) {cachedInstances.putIfAbsent(name, new Holder<Object>());holder = cachedInstances.get(name);}Object instance = holder.get();// 双重检查if (instance == null) {synchronized (holder) {instance = holder.get();if (instance == null) {// 创建拓展实例instance = createExtension(name);// 设置实例到 holder 中holder.set(instance);}}}return (T) instance;}
推荐阅读
- Redisson源码解读-分布式锁
- 红米note9pro评测最新_红米note9pro深度测评
- EasyPoi大数据导入导出百万级实例
- Dubbo-聊聊通信模块设计
- 1分钟完成在线测试部署便捷收集班级同学文件的web管理系统
- 联想拯救者R7000 2021款官方配置_联想拯救者R7000 2021款升级情况
- 简易版 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码
- 三年级数学下册练习题300道 三年级数学计算题300道
- 【lwip】10-ICMP协议&源码分析
- 华为开发者大会2022:HMS Core 3D建模服务再升级,万物皆可驱动