Java基础 -- JDK SPI 概述
JDK SPI(Java Development Kit Service Provider Interface)是Java开发中的一个机制,它规定了如何将服务的实现和服务的使用解耦。在使用JDK SPI之前,程序员需要手动加载对应的实现类,而通过使用JDK SPI,程序员只需要编写服务的接口规范,而不用关心具体的实现类如何加载。
JDK SPI 的核心概念
- 服务接口(Service Interface):服务接口是一个Java接口,用于规定服务实现类的具体实现。
- 服务提供者(Service Provider):服务提供者是实现了服务接口的Java类。
- 服务注册器(Service Registry):服务注册器是一个注册表,用于存储服务提供者的实例。
示例一:实现服务接口
我们以“日志输出”为例进行演示。首先,我们定义一个接口ILogService,用于规范日志输出的规范:
public interface ILogService {
/**
* 输出日志
* @param info
*/
void log(String info);
}
示例二:注册服务提供者
接着,我们需要实现ILogService接口的具体日志输出。这里我们以文件输出作为演示。具体实现如下:
public class FileLogServiceImpl implements ILogService{
@Override
public void log(String info) {
//具体的日志输出实现
System.out.println("日志输出到文件:"+info);
}
}
接下来,我们需要将FileLogServiceImpl注册为ILogService的服务提供者。具体实现如下:
- 在resources/META-INF/services/目录下新建一个以服务接口全限定名命名的文件,例如com.example.ILogService
- 在该文件中,输入服务提供者的全限定名,例如com.example.FileLogServiceImpl
同时,我们也可以编写代码来实现服务提供者的注册过程,如下:
public class LogServiceRegistry {
private static final String SERVICE_PREFIX = "META-INF/services/";
/**
* 注册服务提供者
* @param <T>
* @param serviceType
* @param provider
*/
public static <T> void register(Class<T> serviceType, T provider) {
try {
String serviceTypeName = serviceType.getName();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> enumeration = classLoader.getResources(SERVICE_PREFIX + serviceTypeName);
List<String> providerNames = new LinkedList<>();
while (enumeration.hasMoreElements()) {
URL url = enumeration.nextElement();
InputStream inputStream = url.openStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
providerNames.add(line);
}
}
if (providerNames.isEmpty()) {
throw new RuntimeException("No provider found for " + serviceType.getName());
}
if (providerNames.size() > 1) {
throw new RuntimeException("Multiple providers found for " + serviceType.getName());
}
String providerName = providerNames.get(0);
Class<?> providerClass = Class.forName(providerName, true, classLoader);
if (!serviceType.isAssignableFrom(providerClass)) {
throw new RuntimeException(providerClass.getName() + " not a subtype of " + serviceType.getName());
}
serviceType.cast(provider);
Field providers = serviceType.getDeclaredField("providers");
providers.setAccessible(true);
((List<T>) providers.get(null)).add(provider);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
}
}
最后,在我们需要使用ILogService的地方,只需要通过ServiceLoader对象来获取已经注册的服务提供者即可,示例如下:
public class Main {
public static void main(String[] args) {
ServiceLoader<ILogService> serviceLoader = ServiceLoader.load(ILogService.class);
Iterator<ILogService> iterator = serviceLoader.iterator();
while (iterator.hasNext()) {
ILogService logService = iterator.next();
logService.log("hello world");
}
}
}
总结
通过上面的分析和示例,我们可以看到,JDK SPI机制提供了一种比较优雅的Java服务扩展机制,减轻了开发人员面临多个服务提供者时的负担,其设计思想也是其他Java应用程序中可借鉴的一种可行模式。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java基础–JDK SPI概述 - Python技术站