Java SPI机制

一、软件版本:

Java: 17

二、SPI 概念

SPI的全称是Service Provider Interface,中文名为服务提供者接口,是一种服务发现机制,基于JDK内置的动态加载实现扩展点的机制(通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件,自动加载文件里所定义的类),这一机制使得框架扩展和替换组件变得容易。

三、SPI术语

SPI由三个组件构成:Service、Service Provider、ServiceLoader

  • Service:是一个公开的接口或抽象类,定义了一个抽象的功能模块(文件名称)
  • Service Provider:是Service的实现类(文件内容)
  • ServiceLoader:是SPI机制中的核心组件,负责在运行时发现并加载Service Provider

四、SPI架构和API架构区别

4.1 SPI架构

Java SPI 设计理念是基于接口的编程(策略模式)+配置文件组合实现的动态加载机制

ae3f463cc7a04ae9affcd5121c9eebfa.png

4.2 API架构设置

8500e841adb14e708679405ede410932.png

 SPI的扩展是基于jar级别,API扩展是基于类级别

五 SPI 缺点

  1. Java SPI虽然使用了懒加载机制,但是其获取一个实现时,需要使用迭代器循环加载所有的实现类;
  2. 当需要某一个实现类时,需要通过循环一遍来获取;
  3. 多个并发多线程使用ServiceLoader类的实例是不安全的。

 

六 SPI 创建方式

30b44b5ec70342658466e956fe4cd1f4.png

 

七 SPI-MySql驱动

7.1 MySql驱动的配置22873431b015411fbdf79ebe35f2cb9f.png

  1. 序号1是定义文件夹目录
  2. 序号2是配置文件名称
  3. 序号3是配置文件内容

7.2 MySql驱动的实现类com.mysql.jdbc.Driver

c0795cd035d240369cbe804c5c50c8d0.png

 MySql驱动设置比较巧妙,加载类的时候通过调用类的static代码块来实现注册驱动。

 

7.3 调用入口在DriverManager类的getConnection方法里的ensureDriversInitialized()

1e58f4a647c040ef838be0fec285c08e.png

 7.4 ensureDriversInitialized方法实现SPI11bceb45757c4efda1f93ea334066096.png

 八 自定义SPI

 8.1 创建接口Service项目

8.1.1 创建cssca-message-service

 

 

c89cf27e1c7a40d7ad8ae09075dae6dc.png

8.1.2 创建接口类服务接口类Message,并定义发送信息方法

 

package stu.cacss.service;

public interface Message {
    /**
     * 发送信息方法
     * 
     * @param msg 信息
     */
    void send(String msg);
}

8.1.3 创建获取实现服务的类

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

public abstract class MessageFactory {
    public static Message getMassage() {
        Message message = null;
        ServiceLoader<Message> serviceloader =  ServiceLoader.load(Message.class);
        Iterator<Message> messages = serviceloader.iterator();
        while(messages.hasNext()) {
            message= messages.next();
        }
        return message;
    }
}

8.2 创建Email项目提供Provider

8.2.1 创建项目cssca-email-provider

52a26f49345440f1b61a041ff8570442.png

8.2.2 引入Service接口包

 

        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-message-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <optional>true</optional>
        </dependency>

8.2.3 创建类Email实现接口Message

package stu.sis.impl;

import stu.cacss.service.Message;

public class Email implements Message {

    /**
     * 发送信息方法
     * 
     * @param msg 信息
     */
    public void send(String msg) {
        System.out.println("email: " + msg);
    }

}

8.3 创建Sms项目提供Provider

8.3.1 创建项目cssca-sms-provider

1d97e2f76b7543a983ca5422ab8d03a0.png

8.3.2 引入Service接口包

 

        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-message-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
            <optional>true</optional>
        </dependency>

8.3.3 创建类Sms实现接口Message

package stu.sis.impl;

import stu.cacss.service.Message;

public class Sms implements Message {

    /**
     * 发送信息方法
     * 
     * @param msg 信息
     */
    public void send(String msg) {
        
        System.out.println("Sms: "+msg);
        
    }

}

8.4 创建应用Send项目

8.4.1 创建项目cssca-message-send

7af07684525941b5afee7b7e682a8a65.png

 8.4.2 pom.xml引入Sms的Jar包和Service的Jar包

 

        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-message-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-sms-provider</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

8.4.3 创建测试类Send

package stu.sis;

import stu.cacss.service.Message;
import stu.cacss.service.MessageFactory;

public class Send {

    public static void main(String[] args){
        Message message = MessageFactory.getMassage();
        message.send("Hello World!");
    }

}

8.4.4 运行结果

00dc80c4dda8468e9601ac379be0336d.png

8.4.5 换成Email的Jar包导入

 

成Email的Jar包导入
        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-message-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-email-provider</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

8.4.6 运行测试类

e1b9f006e3b84e0fb2dabb2c9002688b.png

 8.4.7 同时导入两个jar

 

        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-message-service</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-email-provider</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>stu.sis</groupId>
            <artifactId>cssca-sms-provider</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

8.4.8 运行结果

当有多个服务类时,程序获取的是最后一个服务类b5989e48a88c4816a502045590bfab33.png

九 总结

 

  1. 定义接口和发现服务在一个项目工程里,比如java.sql.Driver和java.sql.DriverManager;
  2. 发现服务类有多个时,需要特殊处理,java.sql.DriverManager的发现多个驱动时会跟url匹配,都匹配时取第一个驱动;
  3. 服务类的发现,我们可以采用获取直接获取对象或都采用注册方式,如message= messages.next(),DriverManager.registerDriver();
  4. 服务类发现不是线性安全的,需要多线程处理

 

 

  • 7
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值