我理解的SPI的作用就是根据自己的需要加载外部组件。
JDK中的实现直接参考:Java SPI详解


Spring中的SPI是怎么实现的呢?一开始接触是在springboot的自动装配,加载文件中的自动装配类。其实在springboot启动流程中大量的用到了这个。

核心类:org.springframework.core.io.support.SpringFactoriesLoader
四个方法:

// 加载指定类型的类,并且已经实例化了
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader)// 加载指定的类,返回的是类的全限定类名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader)// 加载所有的类,返回的是类的全限定类名
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader)// 实例化类
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader)

先看SpringFactoriesLoader#loadSpringFactories方法

 private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {// 根据类加载器的不同,分别做了缓存。MultiValueMap<String, String> result = cache.get(classLoader);if (result != null) {return result;}try {// 传入路径,加载所有jar包下该文件// String FACTORIES_RESOURCE_LOCATION = "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()) {// 得到一个文件资源URL url = urls.nextElement();UrlResource resource = new UrlResource(url);// 加载文件,变成Properties 类型Properties properties = PropertiesLoaderUtils.loadProperties(resource);// 遍历properties for (Map.Entry<?, ?> entry : properties.entrySet()) {// key是:指定的类型,value:可以是多个匹配的类型String factoryTypeName = ((String) entry.getKey()).trim();for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {// 放入map中。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);}}

小结:使用类加载器加载所有jar包中的META-INF/spring.factories文件,从文件中解析出key,value放入map中。


如果是多个匹配的类型,使用逗号隔开


SpringFactoriesLoader#loadFactoryNames

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());}

加载指定类型,之前分析了加载所有类型的,只要从所有类型中找出指定的就可以。


SpringFactoriesLoader#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);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;}

加载完指定的类型之后,得到类的全限定类名,实例化类

 @SuppressWarnings("unchecked")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);}}

JDK中的SPI和Spring中的SPI相关推荐

  1. 剖析 SPI 在 Spring 中的应用

    vivo 互联网服务器团队 - Ma Jian 一.概述 SPI(Service Provider Interface),是Java内置的一种服务提供发现机制,可以用来提高框架的扩展性,主要用于框架的 ...

  2. web.xml中的contextConfigLocation在spring中的作用

    在web.xml中通过contextConfigLocation配置spring,contextConfigLocation 参数定义了要装入的 Spring 配置文件. 如果想装入多个配置文件,可以 ...

  3. java中factory_Java后台面试--Spring中FactoryBean与BeanFactory的使用及区别

    以前刚转Java的时候去面试被问到过Spring中FactoryBean与BeanFactory的使用及区别,由于之前没有重视这两个的区别,只是在配置文件里面加bean结点并通过注解的形式调用,所以被 ...

  4. java 调用 spring,java中使用redis和spring中调用redis

    1.需要的jar包,配置的pom.xml文件 redis.clients jedis 2.7.2 2.java调用 /** * @文件名称: JedisTest.java * @描述: TODO * ...

  5. (转)Spring中Singleton模式的线程安全

    不知道哪里的文章,总结性还是比较好的.但是代码凌乱,有的还没有图.如果找到原文了可以进行替换! spring中的单例 spring中管理的bean实例默认情况下是单例的[sigleton类型],就还有 ...

  6. java观察者模式在spring中的应用_Spring源码之spring中的观察者模式和监听器的使用...

    声明:本文根据鲁班学院子路老师spring中观察者模式课程整理得来 观察者模式特点: 被观察者持有监听的观察者的引用. 被观察者支持增加和删除的观察者. 被观察者状态改变通知观察者. JDK中观察者i ...

  7. Spring中Singleton模式的线程安全

    不知道哪里的文章,总结性还是比较好的.但是代码凌乱,有的还没有图.如果找到原文了可以进行替换! spring中的单例 spring中管理的bean实例默认情况下是单例的[sigleton类型],就还有 ...

  8. 面试必杀技,讲一讲Spring中的循环依赖

    本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configu ...

  9. Spring 中的各种注解,光会用可不够哦!

    来源:https://digdeep.cnblogs.com/digdeep/p/4525567.html 1. Java中的注解 2. 使用 元注解 来自定义注解 和 处理自定义注解 3. spri ...

  10. Spring(四)——AOP、Spring实现AOP、Spring整合Mybatis、Spring中的事务管理

    文章目录 1. 什么是AOP 2. 使用Spring实现AOP 2.1 使用Spring的API 接口实现 2.2 自定义实现 2.3 使用注解实现 3. 整合MyBatis 3.1 MyBatis- ...

最新文章

  1. bash脚本编程之for循环
  2. 为什么磁场强度大了呢?
  3. 【Python】纯代码通过神经网络实现线性回归的拟合
  4. 【翻译】关于vertical-align所有你需要知道的
  5. java抽象继承-模板方法
  6. ncbi查找目的基因序列_NCBI大搜索之目的基因寻踪
  7. run as date怎么用_熟词僻义 | date是一种什么水果?
  8. 无线网络受限制或无连接处理方法
  9. php类中引函数变量,一个非线性差分方程的隐函数解
  10. html和body高度不一致,即使html和body都是容器流体的高度不是100%
  11. 雷电游戏java源代码,java雷电游戏源码项目
  12. 破解软件下载网站100个
  13. 【DCANet2022】DCANet: Differential Convolution Attention Network for RGB-D Semantic Segmentation
  14. 怎样学手机拼音打字html t=45,在手机上怎么学拼音打字
  15. 青龙面板搭建及记录踩过的坑
  16. 计算机无法登陆账户 让注销,电脑开机出现登陆账户,点了以后就马上注销,怎么处理?...
  17. HDU oj 自动交题爬虫
  18. 图像预处理——matlab
  19. 以MacOS 13为例,VMware 16安装MacOS
  20. 基于python实现网页版微信API,包含终端版微信及微信机器人

热门文章

  1. linux apache 403 forbidden,apache服务器显示403 Forbidden的原因和解决方法
  2. steam服务器维护6月28,绝地求生6月28日维护更新公告 绝地求生6月28日更新内容汇总...
  3. 判断iOS机器是否支持TouchId, FaceId
  4. python tkinter listbox控件 简书_python tkinter模块的控件操作(1)
  5. 459.重复的子字符串
  6. matplotlib绘制横向柱状图
  7. 凸优化第七章统计估计 7.1参数分布估计
  8. 代码整洁读书笔记---序,前言,代码猴子
  9. 刚才读《基于Lucene的中文自然语言搜索引擎》后感
  10. MongoDB 在windows shell环境下的基本操作和命令的使用示例(三)