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机制的资料请关注脚本之家其它相关文章!

相关文章

  • Java字段初始化的规律解析

    Java字段初始化的规律解析

    这篇文章主要介绍了Java字段初始化的规律解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • Spring Boot集成Thymeleaf的方法

    Spring Boot集成Thymeleaf的方法

    这篇文章主要介绍了Spring Boot集成Thymeleaf的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • 浅谈MyBatis所有的jdbcType类型

    浅谈MyBatis所有的jdbcType类型

    在Mybatis中JdbcType类型是一个枚举类型,它包含了所有的JDBC数据类型,如VARCHAR、INTEGER、DATE等,本文主要介绍了浅谈MyBatis所有的jdbcType类型,具有一定的参考价值,感兴趣的可以了解一下
    2023-06-06
  • springboot项目打包发布部署的过程及jar和war的区别

    springboot项目打包发布部署的过程及jar和war的区别

    Spring Boot使用了内嵌容器,因此它的部署方式也变得非常简单灵活,可以将Spring Boot项目打包成JAR包来独立运行,Spring Boot项目既可以生成WAR包发布,也可以生成JAR包发布,那么它们有什么区别呢
    2022-11-11
  • AgileBoot 项目内统一的错误码设计分析

    AgileBoot 项目内统一的错误码设计分析

    这篇文章主要为大家介绍了AgileBoot 项目内统一的错误码设计分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 十大常见Java String问题_动力节点Java学院整理

    十大常见Java String问题_动力节点Java学院整理

    本文介绍Java中关于String最常见的10个问题,需要的朋友参考下吧
    2017-04-04
  • MybatisPlus处理四种表与实体的映射及id自增策略分析

    MybatisPlus处理四种表与实体的映射及id自增策略分析

    在最近的工作中,碰到一个比较复杂的返回结果,发现简单映射已经解决不了这个问题了,只好去求助百度,学习mybatis表与实体的映射应该怎么写,将学习笔记结合工作碰到的问题写下本文,供自身查漏补缺,同时已被不时之需
    2022-10-10
  • Java JDBC高封装Util类的项目实践

    Java JDBC高封装Util类的项目实践

    这篇文章主要介绍了Java JDBC高封装Util类的项目实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • Intellij Idea中进行Mybatis逆向工程的实现

    Intellij Idea中进行Mybatis逆向工程的实现

    这篇文章主要介绍了Intellij Idea中进行Mybatis逆向工程的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-05-05
  • maven helper jar包冲突的几种解决方法

    maven helper jar包冲突的几种解决方法

    maven Helper是排查jar包冲突的一大利器,jar包冲突大部分是由于引用了同一个jar的不同版本而导致的,本文主要介绍了maven helper jar包冲突的几种解决方法,感兴趣的可以了解一下
    2024-03-03

最新评论