栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

设计原则:API 和 SPI

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

设计原则:API 和 SPI

API
全称:Application Programming Interface 应用程序接口。

SPI
全称:Service Provider Interface,是一种服务发现机制。

面向接口编程:

上图中,我们在“调用方”和“实现方”之间引入了“接口”,但是并没有给出“接口”应该位于哪个位置,单从可能性考虑,有三种情况:
1、“接口”位于“调用方”所在的包中
2、“接口”位于“实现方”所在的包中
3、“接口”位于独立的“包”中。

接下来,我们针对这三种场景进行详细介绍
场景一:接口位于调用方所在的包中
对于类似这种情况下接口,我们将其称为“SPI”,全程为:service provider interface,

SPI的规则如下:
概念上更依赖调用方。
组织上位于调用方所在的包中。
实现位于独立的包中。
常见的例子是:插件模式的插件。

SPI 最常用的场景就是插件的开发。比如eclipse制定接口标准,然后开发人员根据这些接口标准去做具体实现,来开发插件。

SPI 开发步骤:
2.1 服务方提供SPI接口
2.2 提供方(或者调用方)实现接口
2.3 服务方利用服务发现机制,找到SPI接口的实现。

使用场景
概括地说,适用于:调用者根据实际使用需要,启用、扩展、或者替换框架的实现策略

比较常见的例子:
数据库驱动加载接口实现类的加载:JDBC加载不同类型数据库的驱动
日志门面接口实现类加载:SLF4J加载不同提供商的日志实现类
Spring:Spring中大量使用了SPI,比如:对servlet3.0规范对ServletContainerInitializer的实现、自动类型转换Type Conversion SPI(Converter SPI、Formatter SPI)等
Dubbo:Dubbo中也大量使用SPI的方式实现框架的扩展, 不过它对Java提供的原生SPI做了封装,允许用户扩展实现Filter接口

场景二:接口位于实现方所在的包中
我们先想象一个场景,以Unity提供的IUnityContainer接口为例,除了维护这个框架的团队之外,我们没有发现谁实现了这个接口,虽然理论上是可以实现这个接口的(如果能实现的话,我们何不自己弄额Ioc容器呢?)。

对于类似这种情况下的接口,我们将其称作为“API”,“API”的规则如下:

概念上更接近实现方。
组织上位于实现方所在的包中。
实现和接口在一个包中。

场景三:接口位于独立的包中
这里就不说场景了。

如果一个“接口”在一个上下文是“API”,在另一个上下文是“SPI”,那么你就可以这么组织。

spi的使用分为两种,可以使用java内置的spi,也可以使用spring封装的spi。

例子:使用Java内置spi的步骤
1.创建接口和他的多个实现类

package org.spring.springboot.service;

public interface Animal {

    void eat();
}
package org.spring.springboot.service.impl;

import org.spring.springboot.service.Animal;

public class Cat implements Animal {

    @Override
    public void eat() {
        System.out.println("喵喵喵... ...");
    }
}
package org.spring.springboot.service.impl;

import org.spring.springboot.service.Animal;

public class Dog implements Animal {

    @Override
    public void eat() {
        System.out.println("汪汪汪... ...");
    }
}

2.在项目的Resource/meta-INF/Services目录下创建一个和接口全限定名一样的文件,在里面写入接口的实现类的全限类名

3.使用serviceloader加载

public static void main(String[] args) {
        Animal animal = null;
        ServiceLoader serviceLoader = ServiceLoader.load(Animal.class);//查找spi的实现
        Iterator searchs = serviceLoader.iterator();
        while (searchs.hasNext()) {
            animal = searchs.next();
            System.out.println(animal);
        }
    }


可以看到会加载配置文件中写的实现类,并且会加载所有的实现类。

例子:使用Spring spi的步骤
Spring SPI是对java spi进行了封装增强,使用SpringFactories的loadFactories方法可以返回得到配置文件中写的实现类的集合,并且可以选择需要的某个实现类。

Spring中SPI扩展机制原理
在Springboot的自动装配过程中,最终会加载meta-INF/spring.factories文件,而加载的过程是由SpringFactoriesLoader加载的。从CLASSPATH下的每个jar包中搜索所有meta-INF/spring.factories
配置文件,其实这里不仅仅是去ClassPath路径下查询,而是会扫描所有路径下的jar包,只不过这个文件只会在ClassPath下的jar包中.

首先要添加需要的依赖


    org.springframework.boot
    spring-boot-starter
    2.1.6.RELEASE
    true

public static void main(String[] args) {
        //获取spi的所有实现
        List animals = SpringFactoriesLoader.loadFactories(Animal.class, null);
        Animal animal = animals.get(1);
        animal.eat();
    }

查看源码可以发现,对配置文件位置也有要求

对比不难发现,SpringSPI使用起来更方便,实现类的实例都存放在List集合中,调用方可以任意选择。

在项目中,只需要修改调用方的spring.factories文件和引入实现类的jar包,就可以快速选择适合的实现了。

参考资料:
https://blog.csdn.net/Kirito_j/article/details/106446288?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.fixedcolumn&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1.fixedcolumn

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/666199.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号