[关闭]
@Otokaze 2018-12-05T03:31:36.000000Z 字数 1978 阅读 308

Java6 SPI 机制

Java

SPI 简介

Java 6 提供了一个 java.util.ServiceLoader 类,用于实现 Service Provider Interface(SPI)。

SPI 是什么东西呢?以 JDBC 为例子,JDBC 分为两部分,一个是 JDBC API,由 JDK 提供;另一个是 JDBC 驱动实现,由数据库厂商提供。JDBC API 提供了一个数据库驱动接口,java.sql.Driver,Driver 接口的具体实现类则位于不同的数据库厂商 JDBC 驱动中,如 MySQL 的 JDBC 驱动实现类为:com.mysql.jdbc.Driver。

当我们使用 JDBC 访问数据库时,通常的步骤为:

  1. 调用 Class.forName("com.mysql.jdbc.Driver"),注册对应的 JDBC 驱动;
  2. 调用 DriverManager.getConnection(url, user, pass),获取数据库连接。

注意,因为 Class.forName() 中指定了对应的 Driver 实现类的全限定类名,即驱动实现类的类名硬编码在我们的应用程序中,如果后期我们需要更换 Driver 实现类,比如换为 Oracle 的驱动,那么就需要修改程序的代码,这不符合开闭原则。为了解耦,JDK1.6 提供了 SPI 机制,JDBC 4.0 规范开始支持 SPI 机制,这样就不需要在程序中编写 Class.forName() 代码了。

那么 JDBC 4.0 是如何知道要使用什么 java.sql.Driver 实现类呢?很简单,JDK 规定,如果需要使用 SPI 机制,只需要在 ClassPath 路径中的 META-INF/services/java.sql.Driver 文件中放入具体的 java.sql.Driver 实现类的全限定类名就可以了。注意前缀为 META-INF/services/,而 java.sql.Driver 是文件名(UTF-8 编码的文本文件),文件名是有讲究的,即对应的接口名称,而文件内容是按行分割的,每行都是一个对于接口的实现类的全限定类名,比如 com.mysql.jdbc.Driver

JDBC 4.0 在初始化时,会寻找 classpath 中的 META-INF/services/java.sql.Driver 文件(可以有多个,会自动合并在一起),然后实例化这些驱动类,然后我们在调用 DriverManager.getConnection() 方法时,JDBC 4.0 会自动选择合适的驱动实现类(具体如何选择请自行查看 JDBC 4.0 规范以及源码)。

SPI 例子

接口:com.zfl9.service.Service:

  1. package com.zfl9.service;
  2. public interface Service {
  3. void doService();
  4. }

实现类:com.zfl9.service.impl.HelloService:

  1. package com.zfl9.service.impl;
  2. import com.zfl9.service.Service;
  3. public class HelloService implements Service {
  4. @Override
  5. public void doService() {
  6. System.out.println("Hello Service");
  7. }
  8. }

实现类:com.zfl9.service.impl.WorldService:

  1. package com.zfl9.service.impl;
  2. import com.zfl9.service.Service;
  3. public class WorldService implements Service {
  4. @Override
  5. public void doService() {
  6. System.out.println("World Service");
  7. }
  8. }

SPI 文件:META-INF/services/com.zfl9.service.Service:

  1. com.zfl9.service.impl.HelloService
  2. com.zfl9.service.impl.WorldService

Main 类:com.zfl9.service.ServiceMain:

  1. package com.zfl9.service;
  2. import java.util.ServiceLoader;
  3. public class ServiceMain {
  4. public static void main(String[] args) {
  5. ServiceLoader<Service> serviceLoader = ServiceLoader.load(Service.class);
  6. for (Service service : serviceLoader) {
  7. service.doService();
  8. }
  9. }
  10. }

运行结果:

  1. Hello Service
  2. World Service
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注