一、SpringFactoriesLoader 介绍

1.1 SpringFactoriesLoader 简介

SpringFactoriesLoader 工厂加载机制是 Spring 内部提供的一个约定俗成的加载方式,与 java spi 类似,只需要在模块的 META-INF/spring.factories 文件中,以 Properties 类型(即 key-value 形式)配置,就可以将相应的实现类注入 Spirng 容器中。

Properties 类型格式:

key:是全限定名(抽象类|接口)
value:是实现,多个实现通过 **逗号** 进行分隔

1.2 SpringFactoriesLoader 常用方法

  • loadFactoryNames

    读取 classpath上 所有的 jar 包中的所有 META-INF/spring.factories属 性文件,找出其中定义的匹配类型 factoryClass 的工厂类,然后并返回这些工厂类的名字列表,注意是包含包名的全限定名。
  • loadFactories

    读取 classpath 上所有的jar包中的所有 META-INF/spring.factories 属性文件,找出其中定义的匹配类型 factoryClass 的工厂类,然后创建每个工厂类的对象/实例,并返回这些工厂类对象/实例的列表。

1.3 loadFactories 流程图

二、SpringFactoriesLoader 源码解析

2.1 loadFactoryNames 解析

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
// 获取包含包名的工厂类名称
String factoryTypeName = factoryType.getName();
// 获取所有配置在 META-INF/spring.factories 文件的值
// 然后获取指定类的实现类名列表
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// 默认的工厂配置路径地址,可以存放在多个 JAR 包下
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories"; private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 判断是否有缓存结果,如果有直接返回
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
} try {
// 扫描 classpath 上所有 JAR 中的文件 META-INF/spring.factories
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
// 找到的每个 META-INF/spring.factories 文件都是一个 Properties 文件,将其内容加载到一个 Properties 对象然后处理其中的每个属性
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 获取工厂类名称(接口或者抽象类的全限定名)
String factoryTypeName = ((String) entry.getKey()).trim();
// 将逗号分割的属性值逐个取出,然后放到 结果result 中去
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);
}
}
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}

2.2 loadFactories 解析

public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
// 如果未指定类加载器,则使用默认的
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 获取指定工厂名称列表
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
// 如果记录器Trace跟踪激活的话,将工厂名称列表输出
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
// 创建结果集
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
// 实例化工厂类,并添加到结果集中
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
// 对结果集列表进行排序
AnnotationAwareOrderComparator.sort(result);
return result;
}
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}

SpringFactoriesLoader解析的更多相关文章

  1. springboot源码解析 - 构建SpringApplication

    1 package com.microservice.framework; 2 3 import org.springframework.boot.SpringApplication; 4 impor ...

  2. SpringBoot启动流程解析

    写在前面: 由于该系统是底层系统,以微服务形式对外暴露dubbo服务,所以本流程中SpringBoot不基于jetty或者tomcat等容器启动方式发布服务,而是以执行程序方式启动来发布(参考下图ke ...

  3. springboot之启动原理解析

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  4. Spring Boot自动配置源码解析(基于Spring Boot 2.0.2.RELEASE)

    在Spring Boot官方介绍中,首一段话是这样的(如下图).我们可以大概了解到其所表达的含义:我们可以利用Spring Boot写很少的配置来创建一个非常方便的基于Spring整合第三方类库的单体 ...

  5. spring boot(二):启动原理解析

    我们开发任何一个Spring Boot项目,都会用到如下的启动类 @SpringBootApplication public class Application { public static voi ...

  6. SpringBoot的自动配置原理过程解析

    SpringBoot的最大好处就是实现了大部分的自动配置,使得开发者可以更多的关注于业务开发,避免繁琐的业务开发,但是SpringBoot如此好用的 自动注解过程着实让人忍不住的去了解一番,因为本文的 ...

  7. SpringBoot初体验及原理解析

    一.前言 ​ 上篇文章,我们聊到了SpringBoot得以实现的幕后推手,这次我们来用SpringBoot开始HelloWorld之旅.SpringBoot是Spring框架对“约定大于配置(Conv ...

  8. springboot之启动原理解析及源码阅读

    前言 SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面 ...

  9. 【转】Spring Boot干货系列:(三)启动原理解析

    前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开Sprin ...

随机推荐

  1. Springmvc+Mybatis+shiro整合

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大的网络 ...

  2. kafka + spark Streaming + Tranquility Server发送数据到druid

    花了很长时间尝试druid官网上说的Tranquility嵌入代码进行实时发送数据到druid,结果失败了,各种各样的原因造成了失败,现在还没有找到原因,在IDEA中可以跑起,放到线上就死活不行,有成 ...

  3. Longest Increasing Subsequence (Medium)

    第一次做题思路201511092250 1.采用map存储,key为nums[i],value为以nums[i]为结尾的最大递增子序列的长度 2.采用map里面的lower_bounder函数直接找出 ...

  4. 深入JVM内核--GC算法和种类

    GC的概念 Garbage Collection 垃圾收集 1960年 List 使用了GC Java中,GC的对象是堆空间和永久区 引用计数法 老牌垃圾回收算法 通过引用计算来回收垃圾 使用者 CO ...

  5. highcharts 柱状图在柱子顶部显示y轴数据

    var plotOptions={ column:{ //borderColor: "#CCCC66",//边框 shadow: true, //阴影 dataLabels:{ / ...

  6. 应用场景:vue表格撤销删除与保存按钮的显隐

    应用场景:vue表格撤销删除与保存按钮的显隐

  7. jdk源码理解-String类

    String类的理解 简记录一下对于jdk的学习,做一下记录,会持续补充,不断学习,加油 1.String的hash值的计算方法. hash值的计算方法多种多样,jdk中String的计算方法如下,比 ...

  8. 吴裕雄--天生自然 PYTHON语言数据分析:ESA的火星快车操作数据集分析

    import os import numpy as np import pandas as pd from datetime import datetime import matplotlib imp ...

  9. iPhone6爆炸真是小概率事件吗?

    前不久,央视新闻报道,根据上海市消费者权益保护委员会统计,2016年9月到11月,共接到8名消费者投诉,反映其苹果手机在正常使用或者正常充电的情况下突然爆炸.此外,苹果手机还被投诉存在自动关机等问题, ...

  10. 我为什么要用CSDN博客?

    在今年的二月份,因老师说由于学习需要,我怀着抵触的情绪开通了之前闻所未闻的CSDN博客. 三月六号我发了第一篇原创文章,说实话感觉没什么意思,只是在完成老师留给的任务.接下来的几周一直按着老师的要求不 ...