Java SPI(Service Provider Interface)是Java提供的一种服务发现机制,它允许开发者在不修改代码的情况下,动态替换框架或者模块的实现。
场景举例
有一个标准化的软件产品,其中有个功能是通知。但是具体通知形式,由采购方而定。包括但不限于钉钉、企业微信、邮件。那么问题来了,你作为产品设计者,如何设计该通知功能?
实现过程
可以通过Java spi实现该功能,只定义通知,具体实现形式各使用方自由选择。
- 定义服务接口:首先,开发者需要定义一个服务接口,这个接口定义了某种功能或服务的契约。
- 编写服务提供者:开发者可以编写一个或多个服务接口的实现类,这些实现类必须要实现服务接口。
- 配置文件:在资源目录下的META-INF/services目录中,创建以服务接口的完全限定名为文件名的文件。文件内容是服务提供者实现类的完全限定名,每一行对应一个实现类。这个文件用于告诉Java的ServiceLoader类在运行时如何加载服务提供者。
- 使用ServiceLoader:在应用程序中,可以通过ServiceLoader类动态加载服务提供者。通过ServiceLoader可以获取服务接口的实现类的实例,并使用这些实例来完成具体的业务逻辑。
撸代码
- 定义接口
public interface NotificationService {
void sendNotification(String message);
}
- 以邮件通知为例,编写实现类
public class EmailNotificationService implements NotificationService {
@Override
public void sendNotification(String message) {
// 实现具体的邮件通知逻辑
System.out.println("Sending email notification: " + message);
}
}
- 在META-INF/services中注册该实现类,文件名叫
com.example.notification.NotificationService
,文件内容为com.example.notification.EmailNotificationService
- 通过
ServiceLoader
来加载实现类
public class NotificationClient {
public static void main(String[] args) {
ServiceLoader<NotificationService> serviceLoader = ServiceLoader.load(NotificationService.class);
for (NotificationService service : serviceLoader) {
service.sendNotification("Hello, this is a test notification.");
}
}
}
ServiceLoader原理
ServiceLoader是Java提供的用于加载和发现服务的实用工具类。它的工作原理主要包括以下几个步骤:
-
查找配置文件:当调用ServiceLoader.load()方法加载服务时,ServiceLoader会根据服务接口的类加载器(通常是当前线程的上下文类加载器)去查找资源目录下的META-INF/services目录,查找以服务接口的完全限定名为文件名的配置文件。
-
解析配置文件:一旦找到配置文件,ServiceLoader会读取文件内容,文件内容是服务接口的实现类的完全限定名,每行一个实现类。
-
实例化服务提供者:ServiceLoader根据配置文件中的实现类的完全限定名,使用反射机制来实例化这些实现类的对象。
-
返回服务提供者实例:一旦实例化了服务提供者,ServiceLoader将返回一个包含这些服务提供者实例的迭代器,你可以通过迭代器来获取服务提供者的实例,然后使用这些实例来完成具体的业务逻辑。
-
通过这种方式,ServiceLoader实现了动态加载和发现服务的功能,从而使得应用程序可以在运行时动态地加载并替换服务的实现类,而不需要修改代码。
总之,ServiceLoader是一个简单而强大的工具类,它提供了一种松耦合的机制,允许应用程序在运行时发现和使用服务的实现类,从而实现了更好的可插拔性和可扩展性。