@Otokaze
2018-12-05T11:31:36.000000Z
字数 1978
阅读 489
Java
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 访问数据库时,通常的步骤为:
Class.forName("com.mysql.jdbc.Driver")
,注册对应的 JDBC 驱动;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 规范以及源码)。
接口:com.zfl9.service.Service:
package com.zfl9.service;
public interface Service {
void doService();
}
实现类:com.zfl9.service.impl.HelloService:
package com.zfl9.service.impl;
import com.zfl9.service.Service;
public class HelloService implements Service {
@Override
public void doService() {
System.out.println("Hello Service");
}
}
实现类:com.zfl9.service.impl.WorldService:
package com.zfl9.service.impl;
import com.zfl9.service.Service;
public class WorldService implements Service {
@Override
public void doService() {
System.out.println("World Service");
}
}
SPI 文件:META-INF/services/com.zfl9.service.Service:
com.zfl9.service.impl.HelloService
com.zfl9.service.impl.WorldService
Main 类:com.zfl9.service.ServiceMain:
package com.zfl9.service;
import java.util.ServiceLoader;
public class ServiceMain {
public static void main(String[] args) {
ServiceLoader<Service> serviceLoader = ServiceLoader.load(Service.class);
for (Service service : serviceLoader) {
service.doService();
}
}
}
运行结果:
Hello Service
World Service