Java的SPI机制以及基于SPI编程示例详解

 更新时间:2023年08月25日 11:15:37   作者:jacheut  
这篇文章主要为大家介绍了Java的SPI机制以及基于SPI编程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景

在面向对象的设计原则中,一般推荐模块之间基于接口编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现。一旦代码里面涉及具体实现类,就违反了开闭原则。如果需要替换一种实现,就需要修改代码。

为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似 IOC 的思想,将装配的控制权移交到了程序之外。

SPI 英文为 Service Provider Interface, 字面意思就是:“服务提供者的接口”,可以理解为专门提供给服务调用者或者扩展框架功能的开发者去使用的一个接口。

SPI 将服务接口和具体的服务实现分离开来,将服务调用方和服务实现者解耦,能够提升程序的扩展性、可维护性。修改或者替换服务实现并不需要修改调用方。

使用场景

很多框架都使用了 Java 的 SPI 机制,比如:数据库加载驱动,日志接口,以及 dubbo 的扩展实现等等。拿日志接口来说,Spring框架提供的日志服务 SLF4J 其实只是一个日志接口,但是 SLF4J 的具体实现可以有几种,比如:Logback、Log4j、Log4j2 等等,而且还可以切换,在切换日志具体实现的时候我们是不需要更改项目代码的,只需要在 Maven 依赖里面修改一些 pom 依赖就好了。

与API的区别

API:是指可以用来完成某项功能的类、接口或者方法。提供方提供实现方式,调用方只需调用即可。

SPI:是指用来继承、扩展,完成自定义功能的类、接口或者方法。调用方可选择使用提供方提供的内置实现,也可以自己实现。

SPI原理

Java SPI的具体约定为:当服务的提供者提供了服务接口后,在jar包的META-INF/services目录下同时创建一个以服务接口全类名命名的文件,该文件的内容就是实现该服务接口具体实现类的全名,当然这个服务接口实现类也必须在这个jar中。当外部程序装配这个模块的时候,就能通过该jar包META-INF/services下的配置文件找到具体的实现类,并装载实例化,完成模块的注入。Java就是通过扫描META-INF/services文件夹目录下面的文件,把实现类加载到servciceLoader里面。

简单来讲,META-INF/services/下的配置文件名字就是接口的全类名,实现类的全类名就是问这个文件的内容,如果有多个

实现类,可以全部写在这个文件中,同时在实现类中要实现这个接口。

不定义在META-INF/services下面行不行?就想定义在别的地方可以吗?

答案是不行。看下图JDK源码中,因为已经定义为配置路径,如果写在别的地方,类加载器就会找不到了。

案例实现

代码结构如下:

├─main
│ ├─java
│ │ └─com
│ │ ├─test
│ │ │ └─Apple
│ │ │ └─Fruit
│ └─resources
│ ├─META-INF
│ │ └─services
│ │ └─com.test.Fruit

  • 创建接口Fruit,模拟服务提供方接口
package com.test;
public interface Fruit {
}
  • 创建接口实现类Apple,实现Fruit接口;
package com.test;
public class Apple implements Fruit {
    static {
        System.out.println("hi,I am an apple!");
    }
}
  • 在resources结构下创建META-INF/services目录,在这个目录下,创建以这个接口名命名的文件com.test.Fruit,同时在这个文件中写入这个实现类的全类名;
com.test.Apple
  • 创建Test测试类,在测试类中创建一个类加载器ServiceLoader来实现本案例。
import com.test.Fruit;
import java.util.ServiceLoader;
public class Test {
    public static void main(String[] args) {
        ServiceLoader<Fruit> test = ServiceLoader.load(Fruit.class);
        for (Fruit item:test){
        }
    }
}

执行结果如下,表明本案例成功执行。

hi,I am an apple!

以上就是Java的SPI机制以及基于SPI编程示例详解的详细内容,更多关于Java SPI机制的资料请关注脚本之家其它相关文章!

相关文章

  • Java8生成时间方式及格式化时间的方法实例

    Java8生成时间方式及格式化时间的方法实例

    这篇文章主要给大家介绍了关于Java8生成时间方式及格式化时间的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Java面试岗常见问题之ArrayList和LinkedList的区别

    Java面试岗常见问题之ArrayList和LinkedList的区别

    ArrayList和LinkedList作为我们Java中最常使用的集合类,很多人在被问到他们的区别时,憋了半天仅仅冒出一句:一个是数组一个是链表。这样回答简直让面试官吐血。为了让兄弟们打好基础,我们通过实际的使用测试,好好说一下ArrayList和LinkedList的区别这道经典的面试题
    2022-01-01
  • Spring中OpenFeign的使用方法最佳实践

    Spring中OpenFeign的使用方法最佳实践

    这篇文章主要介绍了Spring中OpenFeign使用的相关资料,OpenFeign是一个声明式的WebService客户端,简化了微服务之间的调用,类似于Controller调用Service,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-02-02
  • springboot+vue项目从第一行代码到上线部署全流程

    springboot+vue项目从第一行代码到上线部署全流程

    本文详细介绍了如何从零开始搭建一个基于Spring Boot和Vue.js的前后端分离项目,并涵盖项目需求分析、技术选型、项目结构设计、前后端交互、部署上线等全过程,感兴趣的朋友跟随小编一起看看吧
    2024-11-11
  • MyBatisPlus 主键策略的实现(4种)

    MyBatisPlus 主键策略的实现(4种)

    MyBatis Plus 集成了多种主键策略,帮助用户快速生成主键,本文主要介绍了MyBatisPlus主键策略的实现,具有一定的参考价值,感兴趣的可以了解一下
    2023-10-10
  • 利用session实现简单购物车功能

    利用session实现简单购物车功能

    这篇文章主要为大家详细介绍了利用session实现简单购物车功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Java实现十秒向MySQL插入百万条数据

    Java实现十秒向MySQL插入百万条数据

    这篇文章主要为大家详细介绍了Java如何实现十秒向MySQL插入百万条数据,文中的示例代码讲解详细,对我们学习或工作有一定借鉴价值,需要的可以参考一下
    2022-11-11
  • 为什么Java开发需要配置环境变量

    为什么Java开发需要配置环境变量

    这篇文章主要介绍了为什么Java开发需要配置环境变量,帮助大家更好的理解和学习Java,感兴趣的朋友可以了解下
    2020-08-08
  • Java 空和null的区别

    Java 空和null的区别

    本文主要介绍了Java 空和null的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04
  • SpringBoot MongoCustomConversions自定义转换方式

    SpringBoot MongoCustomConversions自定义转换方式

    这篇文章主要介绍了SpringBoot MongoCustomConversions自定义转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-08-08

最新评论