Java SPI(服务提供商接口)和ServiceLoader
Java SPI(服务提供商接口)是动态加载服务的机制。
通过遵循特定的规则集,我们可以在我们的应用程序中实现Java SPI,并使用ServiceLoader类加载服务。
Java SPI组件
SPI实现中包含四个组件。
服务提供者接口:为服务提供者实现类定义合同的接口或者抽象类。
服务提供者:实际提供服务的实现类。
SPI配置文件:一个特殊的文件,提供了寻找服务实现的逻辑。
文件名必须存在于META-INF/services目录中。
文件名应与服务提供商接口标准名称完全相同。
文件中的每一行都有一个实现服务类详细信息,再次是服务提供者类的完全限定名称。ServiceLoader:Java SPI主类,用于为服务提供商接口加载服务。
ServiceLoader中有多种实用程序方法来获取特定的实现,对其进行迭代或者再次重新加载服务。
Java服务提供者接口示例
java.util.spi
包提供了许多服务提供者接口,可以实现这些接口来提供服务。
ResourceBundleProvider,ResourceBundleControlProvider和AbstractResourceBundleProvider:服务提供程序接口和Resource Bundle实现类的抽象类。
LocaleServiceProvider,CalendarDataProvider,CalendarNameProvider,CurrencyNameProvider,TimeZoneNameProvider和LocaleNameProvider:用于实现特定于区域设置的服务提供者。
Java SPI接口列表
Java SPI示例
让我们创建一个SPI的实现,并使用ServiceLoader类加载一些服务。
1.服务提供商接口
假设我们有一个" MessageServiceProvider"接口,用于定义服务提供商实施的合同。
package com.theitroad.serviceproviders; public interface MessageServiceProvider { void sendMessage(String message); }
2.服务提供商实施类
我们希望支持电子邮件和推送通知消息。
因此,我们将创建" MessageServiceProvider"接口的两个服务提供程序实现– EmailServiceProvider和PushNotificationServiceProvider。
package com.theitroad.serviceproviders; public class EmailServiceProvider implements MessageServiceProvider { public void sendMessage(String message) { System.out.println("Sending Email with Message = "+message); } }
package com.theitroad.serviceproviders; public class PushNotificationServiceProvider implements MessageServiceProvider { public void sendMessage(String message) { System.out.println("Sending Push Notification with Message = "+message); } }
3.服务提供商配置文件
必须在META-INF/services目录中创建配置文件。
其名称应为" com.theitroad.serviceproviders.MessageServiceProvider"。
我们将在此文件中指定两个实现类。
com.theitroad.serviceproviders.EmailServiceProvider com.theitroad.serviceproviders.PushNotificationServiceProvider
4.加载服务的ServiceLoader示例
最后,我们必须使用ServiceLoader类加载服务。
这是一个显示其用法的简单测试程序。
package com.theitroad.test; import java.util.Optional; import java.util.ServiceLoader; import com.theitroad.serviceproviders.MessageServiceProvider; public class ServiceLoaderTest { public static void main(String[] args) { ServiceLoader<MessageServiceProvider> serviceLoader = ServiceLoader.load(MessageServiceProvider.class); for (MessageServiceProvider service : serviceLoader) { service.sendMessage("Hello"); } //using Java 8 Optional to get the first service Optional<MessageServiceProvider> firstService = serviceLoader.findFirst(); if (firstService.isPresent()) { firstService.get().sendMessage("Hello Friend"); } //using Java 8 forEach() method serviceLoader.forEach((service) -> service.sendMessage("Have a Nice Day!")); //Total Number of Loaded Services System.out.println(serviceLoader.stream().count()); } }
当我们运行上面的程序时,我们得到以下输出。
Sending Email with Message = Hello Sending Push Notification with Message = Hello Sending Email with Message = Hello Friend Sending Email with Message = Have a Nice Day! Sending Push Notification with Message = Have a Nice Day! 2
下图显示了我们的最终项目结构和SPI组件。
Java SPI示例项目结构
ServiceLoader重要方法
让我们看一下ServiceLoader类的重要方法。
load():加载特定SPI服务的静态方法。
findFirst():返回可用于该服务提供者的第一个服务。
forEach():在此服务加载器实例中为每个服务提供者运行一些代码很有用。
stream():返回此服务加载器中服务提供者的流。
iterator():返回服务提供者的迭代器。
reload():重新加载服务提供商。
当我们即时更改服务提供商的配置并希望重新加载服务列表时,这很有用。