iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >spring-boot中的SPI机制实例讲解
  • 870
分享到

spring-boot中的SPI机制实例讲解

2024-04-02 19:04:59 870人浏览 泡泡鱼

Python 官方文档:入门教程 => 点击学习

摘要

一、从java类加载机制说起 java中的类加载器负载加载来自文件系统、网络或者其他来源的类文件。JVM的类加载器默认使用的是双亲委派模式。三种默认的类加载器Bootstrap Cl

一、从java类加载机制说起

java中的类加载器负载加载来自文件系统、网络或者其他来源的类文件。JVM的类加载器默认使用的是双亲委派模式。三种默认的类加载器Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(Application ClassLoader)每一个中类加载器都确定了从哪一些位置加载文件。于此同时我们也可以通过继承java.lang.classloader实现自己的类加载器。

Bootstrap ClassLoader:负责加载jdk自带的rt.jar包中的类文件,是所有类加载的父类

Extension ClassLoader:负责加载java的扩展类库从jre/lib/ect目录或者java.ext.dirs系统属性指定的目录下加载类,是System ClassLoader的父类加载器

System ClassLoader:负责从classpath环境变量中加载类文件

1、双亲委派模型

原理:当一个类加载器收到类加载任务时,会先交给自己的父加载器去完成,因此最终加载任务都会传递到最顶层的BootstrapClassLoader,只有当父加载器无法完成加载任务时,才会尝试自己来加载。

具体:根据双亲委派模式,在加载类文件的时候,子类加载器首先将加载请求委托给它的父加载器,父加载器会检测自己是否已经加载过类,如果已经加载则加载过程结束,如果没有加载的话则请求继续向上传递直Bootstrap ClassLoader。

如果请求向上委托过程中,如果始终没有检测到该类已经加载,则Bootstrap ClassLoader开始尝试从其对应路劲中加载该类文件,如果失败则由子类加载器继续尝试加载,直至发起加载请求的子加载器为止。

采用双亲委派模式可以保证类型加载的安全性,不管是哪个加载器加载这个类,最终都是委托给顶层的BootstrapClassLoader来加载的,只有父类无法加载自己猜尝试加载,这样就可以保证任何的类加载器最终得到的都是同样一个Object对象。


protected Class<?> loadClass(String name, boolean resolve) {
    synchronized (getClassLoadingLock(name)) {
    // 首先,检查该类是否已经被加载,如果从JVM缓存中找到该类,则直接返回
    Class<?> c = findLoadedClass(name);
    if (c == null) {
        try {
            // 遵循双亲委派的模型,首先会通过递归从父加载器开始找,
            // 直到父类加载器是BootstrapClassLoader为止
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {}
        if (c == null) {
            // 如果还找不到,尝试通过findClass方法去寻找
            // findClass是留给开发者自己实现的,也就是说
            // 自定义类加载器时,重写此方法即可
           c = findClass(name);
        }
    }
    if (resolve) {
        resolveClass(c);
    }
    return c;
    }
}

2.双亲委派模型缺陷

在双亲委派模型中,子类加载器可以使用父类加载器已经加载的类,而父类加载器无法使用子类加载器已经加载的。这就导致了双亲委派模型并不能解决所有的类加载器问题。

案例:Java 提供了很多服务提供者接口(Service Provider Interface,SPI),允许第三方为这些接口提供实现。

常见的 SPI 有 JDBC、JNDI、JAXP 等,这些SPI的接口由核心类库提供,却由第三方实现,这样就存在一个问题:SPI 的接口是 Java 核心库的一部分,是由BootstrapClassLoader加载的;SPI实现的Java类一般是由AppClassLoader来加载的。

BootstrapClassLoader是无法找到 SPI 的实现类的,因为它只加载Java的核心库。它也不能代理给AppClassLoader,因为它是最顶层的类加载器。也就是说,双亲委派模型并不能解决这个问题

3.使用线程上下文类加载器(ContextClassLoader)加载

如果不做任何的设置,Java应用的线程的上下文类加载器默认就是AppClassLoader。在核心类库使用SPI接口时,传递的类加载器使用线程上下文类加载器,就可以成功的加载到SPI实现的类。线程上下文类加载器在很多SPI的实现中都会用到。

通常我们可以通过Thread.currentThread().getClassLoader()和Thread.currentThread().getContextClassLoader()获取线程上下文类加载器。

4、使用类加载器加载资源文件,比如jar包

类加载器除了加载class外,还有一个非常重要功能,就是加载资源,它可以从jar包中读取任何资源文件,比如,ClassLoader.getResources(String name)方法就是用于读取jar包中的资源文件


//获取资源的方法
public Enumeration<URL> getResources(String name) throws IOException {
    Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
    if (parent != null) {
        tmp[0] = parent.getResources(name);
    } else {
        tmp[0] = getBootstrapResources(name);
    }
    tmp[1] = findResources(name);
    return new CompoundEnumeration<>(tmp);
}

它的逻辑其实跟类加载的逻辑是一样的,首先判断父类加载器是否为空,不为空则委托父类加载器执行资源查找任务,直到BootstrapClassLoader,最后才轮到自己查找。

而不同的类加载器负责扫描不同路径下的jar包,就如同加载class一样,最后会扫描所有的jar包,找到符合条件的资源文件。


// 使用线程上下文类加载器加载资源
public static void main(String[] args) throws Exception{
    // Array.class的完整路径
    String name = "java/sql/Array.class";
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(name);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        System.out.println(url.toString());
    }
}

二、spring中SPI机制实现

1.SPI机制

(1)SPI思想

SPI的全名为Service Provider Interface.这个是针对厂商或者插件的。

SPI的思想:系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。

一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制

(2)SPI约定

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置文件找到具体的实现类名,并装载实例化,完成模块的注入。通过这个约定,就不需要把服务放在代码中了,通过模块被装配的时候就可以发现服务类了。

2、SPI使用案例

common-logging apache最早提供的日志的门面接口。

只有接口,没有实现。具体方案由各提供商实现, 发现日志提供商是通过扫描 META-INF/services/org.apache.commons.logging.LogFactory配置文件,通过读取该文件的内容找到日志提工商实现类。

只要我们的日志实现里包含了这个文件,并在文件里制定 LogFactory工厂接口的实现类即可。

3、SpringBoot中的类SPI扩展机制

在springboot的自动装配过程中,最终会加载META-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。

从CLASSPATH下的每个Jar包中搜寻所有META-INF/spring.factories配置文件,然后将解析properties文件,找到指定名称的配置后返回。

需要注意的是,其实这里不仅仅是会去ClassPath路径下查找,会扫描所有路径下的Jar包,只不过这个文件只会在Classpath下的jar包中。


public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
// spring.factories文件的格式为:key=value1,value2,value3
// 从所有的jar包中找到META-INF/spring.factories文件
// 然后从文件中解析出key=factoryClass类名称的所有value值
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {
    String factoryClassName = factoryClass.getName();
    // 取得资源文件的URL
    Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
    List<String> result = new ArrayList<String>();
    // 遍历所有的URL
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        // 根据资源文件URL解析properties文件,得到对应的一组@Configuration类
        Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url));
        String factoryClassNames = properties.getProperty(factoryClassName);
        // 组装数据,并返回
        result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames)));
    }
    return result;
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: spring-boot中的SPI机制实例讲解

本文链接: https://www.lsjlt.com/news/131104.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • spring-boot中的SPI机制实例讲解
    一、从java类加载机制说起 java中的类加载器负载加载来自文件系统、网络或者其他来源的类文件。jvm的类加载器默认使用的是双亲委派模式。三种默认的类加载器Bootstrap Cl...
    99+
    2024-04-02
  • Java插件扩展机制之SPI案例讲解
    目录什么是SPI与 接口类-实现类 提供的RPC 方式有什么区别?假设我们需要实现RPC,是怎么做的?那RPC究竟跟SPI什么关系?SPI的应用场景怎么实现一个SPI?中间件是怎么实...
    99+
    2024-04-02
  • 深入讲解SPI 在 Spring 中的应用
    目录一、概述二、Java SPI2.1 Java SPI2.2 源码分析三、Dubbo SPI3.1 基本概念3.2 Dubbo SPI3.3 源码分析四、Spring SPI4.1...
    99+
    2024-04-02
  • Spring高手之路14——深入浅出:SPI机制在JDK与Spring Boot中的应用
    文章目录 1. SPI解读:什么是SPI2. SPI在JDK中的应用示例3. SPI在Spring框架中的应用3.1 传统Spring框架中的SPI思想3.2 Spring Boot中的SPI...
    99+
    2023-09-09
    Java SPI spring.factorie Spring Boot自动配置 服务加载机制 Spring Boot高级教程 原力计划
  • Java中的SPI机制案例分享
    目录1 简单介绍2 SPI 案例3 SPI 的原理剖析1 简单介绍 当我们封装了一套接口,其它项目想要调用我们的接口只需要引入我们写好的包,但是其它项目如果想要对我们的接口进行扩展,...
    99+
    2024-04-02
  • Spring Boot应用开发初探与实例讲解
    Spring Boot是由Pivotal团队提供的全新Spring开发框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。 从它的名字可以看出,Spring Boot...
    99+
    2024-04-02
  • spring boot自定义log4j2日志文件的实例讲解
    背景:因为从 spring boot 1.4开始的版本就要用log4j2 了,支持的格式有json和xml两种格式,此次实践主要使用的是xml的格式定义日志说明。spring boot 1.5.8.RELEASE 引入log4j2的开发步骤...
    99+
    2023-05-30
    spring boot 自定义
  • Java中类加载机制的实例讲解
    这篇文章主要介绍“Java中类加载机制的实例讲解”,在日常操作中,相信很多人在Java中类加载机制的实例讲解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中类加载机制的实例讲解”的疑惑有所帮助!接下来...
    99+
    2023-06-20
  • 解析Apache Dubbo的SPI实现机制
    目录一、SPI1.1、JDK自带SPI实现1.2、Dubbo SPI二、加载-ExtensionLoader2.1、获取ExtensionLoader的实例2.2、加载扩展类2.2....
    99+
    2024-04-02
  • Java Spring Dubbo三种SPI机制的区别
    目录前言SPI 有什么用?​JDK SPI​Dubbo SPISpring SPI​对比​前言 SPI 全称为 Service Provider Interface,是一种服务发现机...
    99+
    2024-04-02
  • 如何理解Java中的SPI机制
    本篇内容介绍了“如何理解Java中的SPI机制”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!SPI的概念SPI在Java中的全称为Servi...
    99+
    2023-06-15
  • Python反射机制实例讲解
    目录1. 反射的四个函数2. 类的反射操作3. 当前模块的反射操作4. 其他模块反射操作5. 反射应用场景之一6. 反射应用场景之二7. 总结通常,我们操作对象的属性或者方法时,是通...
    99+
    2024-04-02
  • Java中怎么实现SPI机制
    Java中怎么实现SPI机制,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。2 什么是SPI机制SPI是Service Provider Interface 的简...
    99+
    2023-06-16
  • Spring数据库事务的实现机制讲解
    目录事务控制的核心——Connection用AOP技术保持当前的ConnectionService层和Dao层共享Connection事务为什么要切在Service层的理由sprin...
    99+
    2024-04-02
  • 关于springboot中的SPI机制
    目录一、从java类加载机制说起1、双亲委派模型2、双亲委派模型缺陷3、使用线程上下文类加载器(ContextClassLoader)加载4、使用类加载器加载资源文件,比如jar包二...
    99+
    2024-04-02
  • SpringBoot的SPI机制源码解析
    目录一、从java类加载机制说起1.1 双亲委派模型1.2 双亲委派模型缺陷1.3 使用线程上下文类加载器(ContextClassLoader)加载1.4 使用类加载器加载资源文件...
    99+
    2022-12-21
    SpringBoot SPI机制 SpringBoot SPI
  • Spi机制在Android开发的应用示例详解
    目录Spi机制介绍举个例子ServiceLoader.load在Android中的应用总结Spi机制介绍 SPI 全称是 Service Provider Interface,是一种...
    99+
    2022-11-13
    Android开发Spi机制 Android Spi
  • Spring Boot中使用Spring MVC的示例解析
    目录1.MVC2.Spring MVC3.Spring Boot中使用Spring MVC3.1.配置3.1.1.文件配置3.1.2.代码配置3.2.使用3.2.1.映射处理器3.2...
    99+
    2023-05-15
    Spring Boot使用Spring MVC Spring Boot Spring MVC
  • 一文带你了解Java中的SPI机制
    目录1: SPI机制简介2: SPI原理3: 使用场景4: 源码论证5: 实战6: 优缺点6.1 优点6.2 缺点1: SPI机制简介 SPI 全称是 ...
    99+
    2023-05-15
    Java SPI机制原理 Java SPI机制使用 Java SPI机制
  • Java中的SPI机制是什么
    这篇文章主要介绍“Java中的SPI机制是什么”,在日常操作中,相信很多人在Java中的SPI机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中的SPI机制是什么”的疑惑有所帮助!接下来,请跟...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作