SPI 全称为 (Service Provider Interface) , 是 JDK 内置的一种服务提供发现机制.
为什么需要 SPI?
我们的现代系统越来越庞大, 如果设计架构有问题, 就可能牵一发而动全身, 在面向对象中我们推荐基于接口编程, 模块之间基于接口编程, 这样的好处显而易见, 不在代码中进行硬编码, 不同的实现者按照接口规范实现自己内部操作, 然后在使用的时候再根据 SPI 的规范去获取对应的服务提供者的服务实现. 通过 SPI 服务加载机制进行服务的注册和发现, 可以有效的避免在代码中将服务提供者写死. 从而可以基于接口编程, 实现模块间的解耦.
SPI 机制约定:
1. 在 META-INF/services/ 目录中创建以接口全限定名命名的文件, 该文件内容为 API 具体实 现类的全限定名
2. 使用 ServiceLoader 类动态加载 META-INF 中的实现类
3. 如 SPI 的实现类为 Jar 则需要放在主程序 ClassPath 中
4.API 具体实现类必须有一个不带参数的构造方法
如图:
现在已经被使用的案例:
common-logging Apache 最早提供的日志的门面接口. 只有接口, 没有实现. 具体方 案由各提供商实现, 发现日志提供商是通过扫描 META- INF/services/org.apache.commons.logging.LogFactory 配置文件, 通过读取该文 件的内容 找到日志提工商实现类. 只要我们的日志实现里包含了这个文件, 并在文件里 制定 LogFactory 工厂接口的实现类即可.
JDBC jdbc4.0 以前, 开发人员还需要基于 Class.forName("xxx") 的方式来装载驱动. 创建连接: DriverManage.getConnection() 中, 有 Connection con = aDriver.driver.connect(url, info); driver 成员变量, 是 java.sql.Driver 接口, Java 对外公开的一个加载驱动接口, Java 并未实现, 至于实现这个接口由各个 Jdbc 厂商去实现. 如 MySQL,MySQL-connector-java-5.1.38.jar 包下面 META-INF.services 包下有个 java.sql.Driver 文件打开文件有下面两行 com.MySQL.jdbc.Driver com.MySQL.fabric.jdbc.FabricMySQLDriver
示例 Demo:
创建 maven 项目
目录如下:
代码
OrderService.java
- package com.demo.spi.service;
- public interface OrderService {
- int getOrderCountById(int id);
- }
CustomerOrderServiceImpl.java
- package com.demo.spi.impl;
- import com.demo.spi.service.OrderService;
- public class CustomerOrderServiceImpl implements OrderService {
- public int getOrderCountById(int id) {
- System.out.println("cutomer order count is 10");
- return 10;
- }
- }
AgencyOrderServiceImpl.java
- package com.demo.spi.impl;
- import com.demo.spi.service.OrderService;
- public class AgencyOrderServiceImpl implements OrderService {
- public int getOrderCountById(int id) {
- System.out.println("agency order count is 20");
- return 20;
- }
- }
META-INF 下文件名: com.demo.spi.service.OrderService, 文件内容:
com.demo.spi.impl.AgencyOrderServiceImpl
com.demo.spi.impl.CustomerOrderServiceImpl
来源: https://juejin.im/post/5c66b65d6fb9a04a0164dbea