文章目录
- 什么是SPI
- SPI机制的应用
- 使用方法
- 使用规范
- 入门案例
什么是SPI
SPI首先是一种机制,这个机制叫:服务提供发现机制。那是谁来负责发现呢?当然是JDK内置的服务帮助我们发现啦。发现了帮助我们去调用,我们要做的就是在中间去扩展插件服务。
比如数据库中的java.sql.Driver接口,不同的厂商可以针对同一接口做出不同的实现,如下图所示,MySQL和PostgreSQL都有不同的实现提供给用户。
而Java的SPI机制可以为某个接口寻找服务实现,Java中SPI机制主要思想是将装配的控制权移到程序之外,在模块化设计中这个机制尤其重要,其核心思想就是 解耦。
SPI整体机制图如下:
须知:
- 当服务的提供者提供了一种接口的实现之后,需要在classpath下的 META-INF/services/ 目录里创建一个文件,文件名是以服务接口命名的,而文件里的内容是这个接口的具体的实现类。
- 当其他的程序需要这个服务的时候,就可以通过查找这个jar包(一般都是以jar包做依赖)的META-INF/services/中的配置文件,配置文件中有接口的具体实现类名,再根据这个类名进行加载实例化,就可以使用该服务了。JDK中查找服务的实现的工具类是:java.util.ServiceLoader。
SPI机制的应用
SPI机制的应用—JDBC DriverManager
在JDBC4.0之前,开发连接数据库的时候,通常会用Class.forName("com.mysql.jdbc.Driver")
这句先加载数据库相关的驱动,然后再进行获取连接等的操作。而JDBC4.0之后不需要用Class.forName("com.mysql.jdbc.Driver")
来加载驱动,直接获取连接就可以了,原因就是现在使用了Java的SPI扩展机制来实现。
如上图所示:
- 首先在java中定义了接口 java.sql.Driver,并没有具体的实现,具体的实现都是由不同厂商来提供的。
- 在mysql的jar包mysql-connector-java-8.0.26.jar中,可以找到 META-INF/services 目录,该目录下会有一个名字为 java.sql.Driver 的文件,文件内容是com.mysql.cj.jdbc.Driver,这里面的内容就是mysql针对Java中定义的接口的实现。
- 同样在ojdbc的jar包ojdbc11.jar中,也可以找到同样的配置文件,文件内容是 oracle.jdbc.OracleDriver,这是oracle数据库对Java的java.sql.Driver的实现。
使用方法
而现在Java中写连接数据库的代码的时候,不需要再使用Class.forName("com.mysql.jdbc.Driver")
来加载驱动了,直接获取连接就可以了:
String url = "jdbc:xxxx://xxxx:xxxx/xxxx";
Connection conn = DriverManager.getConnection(url, username, password);
.....
使用规范
入门案例
项目目录如下:
/*** @author linghu* @date 2024/10/28 14:38*/
public interface MessageService {void sendMessage(String message);
}
完成实现类:
/*** @author linghu* @date 2024/10/28 14:39*/
// 电子邮件发送实现
public class EmailMessageService implements MessageService {@Overridepublic void sendMessage(String message) {System.out.println("Sending Email: " + message);}
}
// 短信发送实现
public class SmsMessageService implements MessageService {@Overridepublic void sendMessage(String message) {System.out.println("Sending SMS: " + message);}
}
上面写好路径名,具体的实现类其实就是交给我们自己写的。
测试:
/*** @author linghu* @date 2024/10/28 14:41*/
public class MessTest {public static void main(String []args){ServiceLoader<MessageService> loader = ServiceLoader.load(MessageService.class);for (MessageService service : loader) {service.sendMessage("Hello, SPI!");}}
}
结果:
我们可以继续扩展对吧?扩展实现类,或者说扩展插件。我们做的成本就是自己实现一个实现类,然后再 META-INF/services 目录下的文件补充类路径名,再将当前这个程序打包分享给别人,别人拿到你的jar包就可以自己去扩展了,他只关心扩展,而不用关心调用。