Java的SPI机制

目录

  • 1. 什么是SPI
  • 2. 为什么要使用SPI
  • 3. 关于策略模式和SPI的几点区别
  • 4. 使用介绍或者说约定
    • 4.1 首先介绍几个名词
    • 4.2 约定
  • 5. 具体的demo实现
    • 5.1 创建服务提供者

      • 5.1.1 接口代码如下:
      • 5.1.2 将该项目打包成jar包
    • 5.2 创建服务消费者A
      • 5.2.1 实现接口
      • 5.2.2 按照约定,创建约定目录以及文件
      • 5.2.3 将该工程打包
    • 5.3 创建服务消费者B
      • 5.3.1 实现接口
      • 5.3.2 按照约定,创建约定目录以及文件
      • 5.3.3 将该工程打包
    • 5.4 创建调用者
      • 5.4.1 编写测试调用方法

关于SPI的定义参考博客:https://blog.csdn.net/gallenzhang/article/details/88958800

1. 什么是SPI

SPI是java内置的一种服务发现机制,一般在框架设计的时候,将问题抽象成接口,至于服务的实现,由不同的厂家来各自实现,按照指定的规范引入哪个厂家的实现jar包,就可以使用该厂家的服务实现,这种现象个人理解跟java的多态很类似。

2. 为什么要使用SPI

假设现在没有使用SPI,存在一个接口,和不同的实现类,然后在使用该接口的时候,需要硬编码的形式使用哪个实现类,例如使用工厂模式+策略模式,根据指定条件硬编码,如果我们新增加一种实现,还是需要修改工厂模式的编码,所以跟业务代码存在耦合。而使用SPI机制,可以将第三方的实现方式作为插件的方式,可插拔方式,可以很大程度的与业务代码进行解耦。
但是,这种方式也有缺点,假设配置了多个实现类,不能很好的根据条件的来进行判断筛选,只能针对Iterator遍历获取。

3. 关于策略模式和SPI的几点区别

如果从代码接入的级别来看,策略模式还是在原有项目中进行代码修改,只不过它不会修改原有类中的代码,而是新建了一个类。而 SPI 机制则是不会修改原有项目中的代码,其会新建一个项目,最终以 Jar 包引入的方式代码。

从这一点来看,无论策略模式还是 SPI 机制,他们都是将修改与原来的代码隔离开来,从而避免新增代码对原有代码的影响。但策略模式是类层次上的隔离,而 SPI 机制则是项目框架级别的隔离。

从应用领域来说,策略模式更多应用在业务领域,即业务代码书写以及业务代码重构。而 SPI 机制更多则是用于框架的设计领域,通过 SPI 机制提供的灵活性,让框架拥有良好的插件特性,便于扩展。

  • 从设计思想来看。策略模式和 SPI 机制其思想是类似的,都是通过一定的设计隔离变化的部分,从而让原有部分更加稳定。
  • 从隔离级别来看。策略模式的隔离是类级别的隔离,而 SPI 机制是项目级别的隔离。
  • 从应用领域来看。策略模式更多用在业务代码书写,SPI 机制更多用于框架的设计。

4. 使用介绍或者说约定

4.1 首先介绍几个名词

  • 服务提供者:接口服务提供者,规则定义者,通俗来说,负责写接口
  • 服务消费者:接口服务实现对象,具体的规则实现。
  • 调用方:具体的业务工程,例如在一个Main方法中使用该接口的所在类所在工程。

4.2 约定

  • 定义一个接口
  • 服务消费者所在工程里,在根路径下(针对web项目,可以理解成resources目录下,非web项目,就是src目录下),创建一个文件夹,即META-INF/services, 然后创建一个文件,文件名就是定义的接口的全类名,文件内容就是当前的服务消费者所在工程里针对该接口的实现类对应的全类名。
  • 调用方使用 java.util.ServiceLoader 类来加载服务消费者。

5. 具体的demo实现

在这个demo中,不管是服务提供者还是消费者,以及调用者都是在不同的工程中,不要全部都放在一个工程里,这样比较符合规范和设计,以及能够比较清楚的理解其中的使用细节。
由于是demo,这里就提供一个计算接口,由不同的实现来实现数据的计算,输出结果。

5.1 创建服务提供者

这里创建一个工程 spi-interface,里面专门提供服务接口。

5.1.1 接口代码如下:

package services;

/**
 * @author ztkj-hzb
 * @Date 2020/1/12 14:00
 * @Description
  */
public interface ICalculationService {

    /**
 * 数学计算
  *
 * @param param1
  * @param param2
  * @return
  */
  Object calc(Object param1, Object param2);

}

项目结构如下图:

5.1.2 将该项目打包成jar包

如果不会使用idea或者eclipse打包的,可以百度一下,这里提供一下关于idea打包的参考博客:https://www.cnblogs.com/flyToDreamJava/p/8991201.html

5.2 创建服务消费者A

新建一个工程 spi-impl1,在这里实现了计算接口,使用加法进行数据的运算。注意:这里还需要引入刚才服务提供者的打包好的jar包,不然找不到需要实现的接口。

5.2.1 实现接口

package service.impl;

import services.ICalculationService;

/**
 * @author ztkj-hzb
 * @Date 2020/1/12 14:06
 * @Description
  */
public class CalculationServiceImplB implements ICalculationService {

    /**
 * 加法运算
  * @param o
  * @param o1
  * @return
  */
  @Override
  public Object calc(Object o, Object o1) {
        return (Double)o + (Double)o1;
    }
}

5.2.2 按照约定,创建约定目录以及文件

创建目录 META-INF/services,从上述的服务提供者结构图来看,我们得知接口的全类名是 "services.ICalculationService" , 所以我们需要创建一个文件,名字就是我们的全类名,里面的内容,就是我们刚才的实现类的全类名。

具体的项目结构如下图:

5.2.3 将该工程打包

5.3 创建服务消费者B

服务消费者B项目结构跟A几乎一致,本不想重复写,考虑有些朋友跟我一样是新手,所以就多写一点。
新建一个工程 spi-impl2,在这里实现了计算接口,使用减法进行数据的运算。再次提醒注意:这里还需要引入刚才服务提供者的打包好的jar包,不然找不到需要实现的接口。

5.3.1 实现接口

package service.impl;

import services.ICalculationService;

/**
 * @author ztkj-hzb
 * @Date 2020/1/12 14:08
 * @Description
  */
public class CalculationServiceImplA implements ICalculationService {

    /**
 * 减法运算
  *
 * @param o
  * @param o1
  * @return
  */
  @Override
  public Object calc(Object o, Object o1) {
        return (Double) o - (Double) o1;
    }
}

5.3.2 按照约定,创建约定目录以及文件

创建目录 META-INF/services,从上述的服务提供者结构图来看,我们得知接口的全类名是 "services.ICalculationService" , 所以我们需要创建一个文件,名字就是我们的全类名,里面的内容,就是我们刚才的实现类的全类名。

具体的项目结构如下图:

5.3.3 将该工程打包

5.4 创建调用者

创建一个工程 spi-main, 注意:需要引用上面三个项目的jar包。

5.4.1 编写测试调用方法

package com.lonely;

import services.ICalculationService;

import java.util.Iterator;
import java.util.ServiceLoader;

/**
 * @author ztkj-hzb
 * @Date 2020/1/12 14:18
 * @Description
  */
public class Main {

    public static void main(String[] args) {
        ServiceLoader<ICalculationService> serviceLoader = ServiceLoader.load(ICalculationService.class);
        Iterator<ICalculationService> iterator = serviceLoader.iterator();
        while(iterator.hasNext()){
            ICalculationService calculationService = iterator.next();
            System.out.println(MessageFormat.format("指定类:{0}.calc()执行结果是:{1}",calculationService.getClass().getName(),calculationService.calc(23.1,15.3)));
        }
    }
}

执行结果如下:

指定类:service.impl.CalculationServiceImplA.calc()执行结果是:7.8
指定类:service.impl.CalculationServiceImplB.calc()执行结果是:38.4

从结果上看,我们在调用方工程代码中,没有写上一行关于 "CalculationServiceImplA"或者"CalculationServiceImplB"相关服务的硬代码,只要后续新添加一种实现,只需要在编写一个工程,按照SPI约定,打包成jar后,放入到调用者工程中,就能使用了,完全与具体实现解耦。

原文地址:https://www.cnblogs.com/duguxiaobiao/p/12183135.html

时间: 2024-08-30 05:12:10

Java的SPI机制的相关文章

java 的SPI机制

今天看到spring mvc 使用Java Validation Api(JSR-303)进行校验,需要加载一个 其具体实现(比如Hibernate Validator), 本来没有什么问题,但是突然想到这其中到底是怎样一种加载过程呢,也就是说spring为什么能够找到Hibernate Validator来作为JSR-303的具体实现的呢? 1. java中的SPI机制 这篇文章对java的SPI机制讲的比较容易理解,就不多做记录. http://www.cnblogs.com/javaee6

Java的SPI机制与简单的示例

一.SPI机制 这里先说下SPI的一个概念,SPI英文为Service Provider Interface单从字面可以理解为Service提供者接口,正如从SPI的名字去理解SPI就是Service提供者接口:我对SPI的定义:提供给服务提供厂商与扩展框架功能的开发者使用的接口. 在我们日常开发的时候都是对问题进行抽象成Api然后就提供各种Api的实现,这些Api的实现都是封装与我们的Jar中或框架中的虽然当我们想要提供一种Api新实现时可以不修改原来代码只需实现该Api就可以提供Api的新实

JAVA中SPI机制

之前研究dubbo的时候就很好奇,里面各种扩展机制,期间也看过很多关于SPI的机制,今日有缘再度看到有文章总结,故记录一下, 首先了解一下 JAVA中SPI简单的用法 可参考这篇文章,https://mp.weixin.qq.com/s/vpy5DJ-hhn0iOyp747oL5A 应用场景 SPI扩展机制应用场景有很多,比如Common-Logging,JDBC,Dubbo等等. SPI流程: 有关组织和公式定义接口标准 第三方提供具体实现: 实现具体方法, 配置 META-INF/servi

高级开发必须理解的Java中SPI机制

https://www.jianshu.com/p/46b42f7f593c 本文通过探析JDK提供的,在开源项目中比较常用的Java SPI机制,希望给大家在实际开发实践.学习开源项目提供参考. 1 SPI是什么 SPI全称Service Provider Interface,是Java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件. 整体机制图如下: Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制. 系统设计的各个抽象,往

Java的SPI机制示例代码

定义接口 package com.hiwzc.myspi; public interface Demo {     void dosomething(); } 提供两种模拟实现 package com.hiwzc.myspi.impl; import com.hiwzc.myspi.Demo; public class DemoAImpl implements Demo{     @Override     public void dosomething() {         System.o

java基础 - spi机制

(待补充) --- 参考: https://www.jianshu.com/p/46b42f7f593c https://blog.csdn.net/yangguosb/article/details/78772730 原文地址:https://www.cnblogs.com/lankerenf3039/p/12121190.html

java中的SPI机制

1.定义 SPI 全称为 (Service Provider Interface) ,是JDK内置的一种服务提供发现机制. 一个服务(Service)通常指的是已知的接口或者抽象类,服务提供方就是对这个接口或者抽象类的实现,然后按照SPI 标准存放到资源路径META-INF/services目录下,文件的命名为该服务接口的全限定名. 2.SPI机制的约定 在META-INF/services/目录中创建以Service接口全限定名命名的文件,该文件内容为Service接口具体实现类的全限定名,文

你应该了解的 Java SPI 机制

前言 不知大家现在有没有去公司复工,我已经在家办公将近 3 周了,同时也在家呆了一个多月:还好工作并没有受到任何影响,我个人一直觉得远程工作和 IT 行业是非常契合的,这段时间的工作效率甚至比在办公室还高,同时由于我们公司的业务在海外,所以疫情几乎没有造成太多影响. 扯远了,这次主要是想和大家分享一下 Java 的 SPI 机制.周末没啥事,我翻了翻我之前的写的博客 <设计一个可拔插的 IOC 容器>,发现当时的实现并不那么优雅. 还没看过的朋友的我先做个前景提要,当时的需求: 我实现了一个类

jdk和dubbo的SPI机制

前言:开闭原则一直是软件开发领域中所追求的,开闭原则中的"开"是指对于组件功能的扩展是开放的,是允许对其进行功能扩展的,“闭”,是指对于原有代码的修改是封闭的,即不应该修改原有的代码.对于一个高度集成化的.成熟.稳健的系统来讲,永远不是封闭.固守的,它需要向外提供一定的可扩展的能力,外部的实现类或者jar包都可以调用它.在面向对象的开发领域中,接口是对系统功能的高度抽象,因为SPI可谓是"应运而生",本篇博客就开始走进SPI,探究java自身的SPI和Dubbo的S