java SPI机制的使用及原理

news2024/11/25 16:42:45

本片文章是针对dubbo SPI机制深入分析的平滑过渡的作用。当然咱们主要是学习优秀的思想,SPI就是一种解耦非常优秀的思想,我们可以思考在我们项目开发中是否可以使用、是否可以帮助我们解决某些问题、或者能够更加提升项目的框架等

一、SPI是什么

SPI(service provider interface)是java提供的一套用来被第三方实现或者扩展的API,它可以用来启用框架扩展和替换组件。

如果用上面这句话来描述SPI那么是一点卵用没有,下面用生动的例子来阐述。其实SPI跟我们的策略设计模式比较相似,如果对策略设计模式不太了解的,可以先花点时间去学习一下。

实例:假如,我们在京东上购买商品需要付款,假如我们可以选择的支付的模块有支付宝、微信、银行卡。如果我们使用策略设计模式的话,简单的代码如下。

/*** * 抽象支付 */
public interface Pay {
   

    void pay();
}
/** * @Auther: * @Date: 2020/5/2 14:28 * @Description: 支付宝付款 */
public class AliPay implements Pay {
   
    @Override
    public void pay() {
   
        System.out.println("使用支付宝pay....");
    }
}
/** * @Auther: * @Date: 2020/5/2 14:28 * @Description: 微信pay */
public class WechatPay implements Pay {
   
    @Override
    public void pay() {
   
        System.out.println("使用微信支付....");
    }
}
/** * @Auther: * @Date: 2020/5/2 14:29 * @Description:银行卡pay */
public class BankCardPay implements Pay {
   
    @Override
    public void pay() {
   
        System.out.println("使用银行卡支付....");
    }
}

你可以根据你的需求创建出相应的Pay的实现类,然后调用pay(),例如我简写一下

/**
 * @Auther:
 * @Date: 2020/5/2 14:36
 * @Description:
 */
public class Context {


    private final Pay pay;

    public Context(Pay pay) {
        this.pay = pay;
    }

    public void invokeStrategy(){
        pay.pay();
    }
}
/** * @Auther: * @Date: 2020/5/2 14:35 * @Description: */
public class PayTest {
   

    public static void main(String[] args) {
   
        //ali
        Context aliContext = new Context(new AliPay());
        aliContext.invokeStrategy();
        //wechat
        Context wechatContext = new Context(new WechatPay());
        wechatContext.invokeStrategy();
    }
}

从上面的代码中我们其实可以看到还是需要我们显示的创建出相应的支付模块。

二、SPI如何使用

那么现在有这样的场景:当我的项目里面有什么支付模块我就使用什么样的支付模块,比如说有支付宝支付模块就选择支付宝、有微信支付模块我就选择微信支付、同时有多个的时候,我默认选择第一个,此时我们就可以使用SPI,先看下如何使用。

1、创建META-INF/services文件夹,然后创建一个以Pay接口全限定名为名字的文件

2、在文件中编写想要实现哪个Pay的实现类(AliPay,WechatPay,BankCardPay),注意也要是全限定名

假如是是支付宝支付的模块,上面文件的内容:

com.taolong.dubbo.spi.strategy.AliPay

3、获取Pay并调用

获取并调用的逻辑,我就修改下上面的策略模式中的Context的invokerStrategy方法,这里假设默认使用第一个

public void invokeStrategy(){
   
    ServiceLoader<Pay> payServiceLoader = ServiceLoader.load(Pay.class);
    Iterator<Pay> iterator = payServiceLoader.iterator();
    if (iterator.hasNext()){
   
        iterator.next().pay();
    }
}

main方法调用

 public static void main(String[] args) {
   
// //ali
// Context aliContext = new Context(new AliPay());
// aliContext.invokeStrategy();
// //wechat
// Context wechatContext = new Context(new WechatPay());
// wechatContext.invokeStrategy();
        Context context = new Context();
        context.invokeStrategy();
    }

上面就是使用的SPI机制,让其选择一个Pay的实现类,这样子就比较灵活了,比如正常的团队工作情况是下面这样子。

A团队:负责支付宝支付支付模块的开发

B团队:负责微信支付模块的开发

C团队:负责银行卡支付模块的开发

此时A团队的支付模块里面只需要新建一个“com.taolong.dubbo.spi.strategy.Pay”的文件,文件的内容对应Alipay(他们自己命名),然后编写自己的支付逻辑即可

B团队也只需要新建“com.taolong.dubbo.spi.strategy.Pay”文件的内容比如是WechatPay(他们自己命名),然后编写自己的逻辑即可。

相当于Pay定义了一个规范,不管是微信、还是支付宝支付只要符合这个规范就行。在使用的使用,我们只需要加入相应的依赖(比如支付宝模块的pom依赖,微信模块的pom依赖),项目就能自动发现具体的实现类,然后调用相应的模块的支付方法。

三、SPI的优秀实现案例

如果对我上面的描述不太理解的话,我们来看一个真实的使用上述SPI的例子—数据库驱动(Driver)

我们知道,当我们的项目里面使用引用了mysql的驱动pom依赖时,我们的项目里面会自动选择使用mysql的驱动,我们甚至不需要手动去加载。我们来看看它的具体实现。

1、首先看一下java.sql.Driver的类

这里面也是相当于定义了一个规范

2、其次看mysql驱动包的META-INF/services文件夹下面有没有指定的文件

很熟悉,命名就是java.sql.Driver

3、打开文件查看一下文件内容

这里面就能看到我们的mysql的驱动了,到这里基本上就确认这也是使用SPI实现的,顺便说一下,现在为什么我们不需要使用Class.forName()去加载驱动了,这是因为DriverManager使用SPI的机制已经帮我们加载好了,我们来看看DriverManager的类

(1)静态代码块

/** * Load the initial JDBC drivers by checking the System property * jdbc.properties and then use the {@code ServiceLoader} mechanism */
static {
   
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}

oadInitialDrivers()

private static void loadInitialDrivers() {
   
    String drivers;
    try {
   
        drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
   
            public String run() {
   
                return System.getProperty("jdbc.drivers");
            }
        });
    } catch (Exception ex) {
   
        drivers = null;
    }
    // If the driver is packaged as a Service Provider, load it.
    // Get all the drivers through the classloader
    // exposed as a java.sql.Driver.class service.
    // ServiceLoader.load() replaces the sun.misc.Providers()

    AccessController.doPrivileged(new PrivilegedAction<Void>() {
   
        public Void run() {
   

            //重点看这里
            ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
            Iterator<Driver> driversIterator = loadedDrivers.iterator();

            /* Load these drivers, so that they can be instantiated. * It may be the case that the driver class may not be there * i.e. there may be a packaged driver with the service class * as implementation of java.sql.Driver but the actual class * may be missing. In that case a java.util.ServiceConfigurationError * will be thrown at runtime by the VM trying to locate * and load the service. * * Adding a try catch block to catch those runtime errors * if driver not available in classpath but it's * packaged as service and that service is there in classpath. */
            try{
   
                while(driversIterator.hasNext()) {
   
                    driversIterator.next();
                }
            } catch(Throwable t) {
   
            // Do nothing
            }
            return null;
        }
    });

    println("DriverManager.initialize: jdbc.drivers = " + drivers);

    if (drivers == null || drivers.equals("")) {
   
        return;
    }
    String[] driversList = drivers.split(":");
    println("number of Drivers:" + driversList.length);
    for (String aDriver : driversList) {
   
        try {
   
            println("DriverManager.Initialize: loading " + aDriver);
            Class.forName(aDriver, true,
                    ClassLoader.getSystemClassLoader());
        } catch (Exception ex) {
   
            println("DriverManager.Initialize: load failed: " + ex);
        }
    }
}

很明显就能看到它的调用方法跟我们上面将的例子是一样的,唯一不同的是它会加载所有的驱动。

不仅仅是数据库驱动使用了SPI,还有slf4j、spring等等

上面的java的SPI的源码本文限于篇幅,就不讲解了,感兴趣的可以自行阅读,不过,我们也能够猜测出它的主要的逻辑,下面用一副简单的图来描述一下

不管是文件名还是文件内容都是全限定名,所以通过反射很容易创建相应的类

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/92804.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新冠病毒:KN95(GB2626类型口罩)是否有效阻挡?

点击上方“青年码农”关注回复“源码”可获取各种资料​今天刷新闻&#xff0c;看到很多官方账号发布&#xff0c;只有五种编码口罩能防疫&#xff0c;分别是医用防护口罩&#xff08;GB19083-2010&#xff09;医用外科口罩&#xff08;YY0469-2011&#xff09;一次性使用医用口…

华纳音乐集团 Game Jam 来啦!

为了给 2022 年画上一个完美的句点&#xff0c;The Sandbox 与华纳音乐集团合作&#xff0c;为你们带来本年度的最后一次 Game Jam&#xff01; 我们邀请 The Sandbox 用户以音乐为题创建游戏体验。你们可以自由地创造社交体验&#xff0c;但也可以创造具有故事情节的游戏。请给…

云原生|kubernetes|CKA模拟测试-2022(1---10题)(一)

第一题&#xff1a; Task weight: 1% You have access to multiple clusters from your main terminal through kubectl contexts. Write all those context names into /opt/course/1/contexts. Next write a command to display the current context into /opt/course/1/c…

【反外挂】内存加密与监视

前言 手游防破解防外挂技术方案&#xff08;一&#xff09;客户端篇 各种作弊方案中&#xff0c;其中一种是直接修改内存数据&#xff0c;如下。 若要修改玩家当前的金币数&#xff0c;先用工具在内存中搜索当前的金币数值&#xff0c;会搜出来很多内存地址。然后消耗一些金币…

Java集合概述

集合概述 集合是一个容器,是一个载体,可以一次容纳多个对象。前面学习的数组其实就是一个集合。 集合不能直接存储基本数据类型&#xff0c;基本数据类型都是经过自动装箱后变成包装类型存放的&#xff1b; 集合也不能直接存储Java对象&#xff0c;集合中存储的是Java对象的内…

扫码点餐小程序源码 多商户外卖点餐自助扫码预约源码

智慧餐厅扫码点餐小程序系统源码&#xff0c;二维码点餐&#xff0c;微信支付宝点餐系统源码&#xff0c;外卖点餐源码 1. 开发语言&#xff1a;JAVA 2. 数据库&#xff1a;MySQL 3. 原生小程序 4. Sass 模式 5. 带调试视频 6. 可付费调试服务 私信了解更多&#xff01;…

VCS3 debug的基础

1、基础知识 使用命令行进行debug。 使用VCS进行debug的三种方式&#xff1a;专门做debug的工具目前最好的是Verdi 1、系统函数的调用 2、通过命令行的方式 3、使用DVE(GUI) debug需要注意的因素&#xff1a; 1、仿真速度&#xff08;开关选项&#xff08;command_time\ru…

超长距离CDN类视频直播延时估算

超长距离RTMP视频直播延时估算值。 摘录内容如下&#xff1a; 简单估算一下大概的网络延时。众所周知&#xff0c;光在真空中的速度约为300,000km/s&#xff0c;而在其他介质中光速会大大降低&#xff0c;所以在普通光纤中&#xff0c;工程上一般认为传输速度是200,000km/s。…

jsp+ssm计算机毕业设计ssm新冠疫苗预约接种信息管理【附源码】

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JSPSSM mybatis Maven等等组成&#xff0c;B/S模式 Mave…

Pytest框架测试用例规则和运行方式

目录 一、默认的测试用例规则 二、测试用例执行顺序 三、测试用例运行方式 3.1.主函数模式 3.1.1.主函数模式&#xff1a;4种运行方式 3.1.2.文件框架如下图 3.2.命令行模式 3.2.1.命令行模式&#xff1a;4种运行方式 3.2.2.第2种运行方式框架 3.3.通过读取配置文…

【JAVA】抽象类和接口

&#x1f3c6;今日学习目标&#xff1a;抽象类和接口 &#x1f603;创作者&#xff1a;颜颜yan_ ✨个人主页&#xff1a;颜颜yan_的个人主页 ⏰本期期数&#xff1a;第二期 &#x1f389;专栏系列&#xff1a;JAVA 文章目录一、抽象类抽象类的定义规则示例二、接口接口定义与语…

Volatile关键字简述

Volatile关键字前言前置知识程序、进程、线程程序进程线程并发所涉及的一些特性线程安全原子性可见性Volatile案例环境代码展示可见性测试原子性测试前言 最近在看《Java并发编程实战》&#xff0c;期望对一些并发的知识点做一些总结。最好有一定的Java基础、并发的基础。 前…

Qt 一个信号对应多个槽,多个信号对应一个槽的执行顺序

前言&#xff1a; Qt独创的信号槽机制&#xff0c;不仅可以一个信号连接一个槽&#xff0c;而且可以一对多或多对一。 这其中存在两个最基本的问题&#xff1a; 1.一个信号对应多个槽时&#xff0c;槽函数的执行顺序是怎样的&#xff1f;&#xff1f;&#xff1f; 2.多个信号对…

【操作系统三】图解网络IO(bio\nio\slect\epoll)

【操作系统三】图解网络IO实战一、计算机组成二、系统中断三、晶振&#xff08;时间中断、分时复用&#xff09;四、事件中断1、DMA2、事件中断3、网卡也会产生中断&#xff1f;五、linux系统知识1、linux下一切皆文件&#xff1f;1.1、nc启动一个服务端,端口号80801.2、linux下…

vulnhub靶机:matrix:1

目录 查看靶机的ip 开放端口扫描 解密 字典生成爆破 ssh登录 rbash逃逸 下载地址&#xff1a;Matrix: 1 ~ VulnHub Kali地址&#xff1a;192.168.174.128 靶机地址&#xff1a;192.168.174.139 这个靶机是我在刷到的一个视频&#xff0c;讲解这个靶机的博主比较有趣&a…

web网页设计期末课程大作业:水果网站设计——HTML+CSS+JavaScript水果超市(带论文)

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

大一学生Web课程设计 HTML+CSS保时捷汽车介绍(可以很好的应付老师的作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

2022CTF培训(八)ARM PWN 环境搭建ARM PWN 入门

附件下载链接 环境搭建 QEMU qemu是一款可执行硬件虚拟化的虚拟机&#xff0c;与他类似的还有Bochs、PearPC&#xff0c; 但qemu具有高速&#xff08;配合KVM&#xff09;、跨平台的特性 qemu主要有两种运行模式&#xff1a;qemu-user 和 qemu-system qemu-system 可以进行…

[附源码]Python计算机毕业设计高校互联网班级管理系统Django(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程 项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等…

[附源码]Nodejs计算机毕业设计基于的开放式实验室预约系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置&#xff1a; Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分…