目录
- 前言
- 1. 基本知识
- 2. Demo
- 3. 解读源码
前言
相关的Java知识推荐阅读:
- java框架 零基础从入门到精通的学习路线 附开源项目面经等(超全)
- 【Java项目】实战CRUD的功能整理(持续更新)
1. 基本知识
SPI(Service Provider Interface) 是一种服务发现机制,允许 Java 应用程序动态地加载和使用服务实现
SPI 机制是 Java 提供的服务加载机制的一部分,可以在运行时找到和加载实现接口的服务提供者,而不需要在编译时将这些实现硬编码到代码中
基本的知识点如下:
-
定义 SPI 接口:SPI 需要定义一个接口或抽象类,作为服务的规范
-
提供服务的实现:创建实现该接口的具体服务实现类
-
配置服务提供者: 在 META-INF/services/ 目录下创建一个文件,文件名为接口的完全限定名,文件内容是实现该接口的类的完全限定名
-
加载服务实现:使用 ServiceLoader 类来动态加载服务实现
了解基本的知识点,结合Demo进行了解
2. Demo
定义服务接口:
public interface GreetingService {
void greet(String name);
}
提供服务的实现类1:
public class EnglishGreetingService implements GreetingService {
@Override
public void greet(String name) {
System.out.println("Hello, " + name);
}
}
实现类2:
public class SpanishGreetingService implements GreetingService {
@Override
public void greet(String name) {
System.out.println("Hola, " + name);
}
}
配置服务提供者:
在 META-INF/services/ 目录下创建一个文件 com.example.GreetingService,内容为服务实现的完全限定名:
com.example.EnglishGreetingService
com.example.SpanishGreetingService
最终加载服务实现类:
import java.util.ServiceLoader;
public class GreetingServiceLoader {
public static void main(String[] args) {
// 使用 ServiceLoader 加载 GreetingService 接口的所有实现类
ServiceLoader<GreetingService> loader = ServiceLoader.load(GreetingService.class);
// 遍历所有加载的服务实现
for (GreetingService service : loader) {
// 调用服务实现的方法
service.greet("码农研究僧");
}
}
}
执行截图如下:
之所以读取此处的配置文件,可以通过源码查看:
基本的运行机制如下:
-
编写服务接口:
定义一个接口或抽象类,该接口定义了服务提供者需要实现的功能 -
实现服务接口:
创建一个或多个类实现该服务接口
这些类是服务的实际提供者 -
配置服务提供者:
在META-INF/services/
目录下创建一个文件,文件名为接口的完全限定名(即接口的全类名)
在文件中列出所有实现该接口的服务提供者的完全限定名,每行一个类名 -
运行时加载服务:
4.1 当应用程序运行并调用ServiceLoader.load(GreetingService.class)
时,ServiceLoader 会读取 META-INF/services/com.example.GreetingService 文件,获取所有服务实现的类名
ServiceLoader 通过反射机制动态加载这些类,并创建它们的实例
4.2 使用反射(Class.forName())
加载服务实现类,通过Class.newInstance()
创建服务实例,将服务实例缓存到 providers 中 -
使用服务:
使用 ServiceLoader 的iterator()
方法遍历所有实现,并调用它们的方法
3. 解读源码
解读部分源码:
reload方法:
- 清空缓存的服务提供者 providers
- 重新创建一个 LazyIterator 实例,用于重新查找和加载服务实现
public void reload() {
providers.clear();
lookupIterator = new LazyIterator(service, loader);
}
parse方法:
- parse() 方法读取并解析配置文件中的服务实现类名
- 使用 BufferedReader 按行读取文件,并调用 parseLine() 处理每一行
- 将有效的类名添加到 names 列表中,并返回一个迭代器
private Iterator<String> parse(Class<?> service, URL u) throws ServiceConfigurationError {
InputStream in = null;
BufferedReader r = null;
ArrayList<String> names = new ArrayList<>();
try {
in = u.openStream();
r = new BufferedReader(new InputStreamReader(in, "utf-8"));
int lc = 1;
while ((lc = parseLine(service, u, r, lc, names)) >= 0);
} catch (IOException x) {
fail(service, "Error reading configuration file", x);
} finally {
try {
if (r != null) r.close();
if (in != null) in.close();
} catch (IOException y) {
fail(service, "Error closing configuration file", y);
}
}
return names.iterator();
}
parseLine方法:
- parseLine() 方法处理服务配置文件中的每一行
- 忽略注释(以 # 开头的行)和空行
- 验证类名是否合法,并将有效的类名添加到 names 列表
private int parseLine(Class<?> service, URL u, BufferedReader r, int lc, List<String> names) throws IOException, ServiceConfigurationError {
String ln = r.readLine();
if (ln == null) {
return -1;
}
int ci = ln.indexOf('#');
if (ci >= 0) ln = ln.substring(0, ci);
ln = ln.trim();
int n = ln.length();
if (n != 0) {
if ((ln.indexOf(' ') >= 0) || (ln.indexOf('\t') >= 0))
fail(service, u, lc, "Illegal configuration-file syntax");
int cp = ln.codePointAt(0);
if (!Character.isJavaIdentifierStart(cp))
fail(service, u, lc, "Illegal provider-class name: " + ln);
for (int i = Character.charCount(cp); i < n; i += Character.charCount(cp)) {
cp = ln.codePointAt(i);
if (!Character.isJavaIdentifierPart(cp) && (cp != '.'))
fail(service, u, lc, "Illegal provider-class name: " + ln);
}
if (!providers.containsKey(ln) && !names.contains(ln))
names.add(ln);
}
return lc + 1;
}
LazyIterator 内部类:
- LazyIterator 负责延迟加载服务提供者
- hasNextService() 方法查找和加载服务配置文件中的服务实现类
- nextService() 方法使用反射创建服务实例,并返回它
- hasNext() 和 next() 方法实现了 Iterator 接口,用于遍历服务提供者
private class LazyIterator implements Iterator<S> {
Class<S> service;
ClassLoader loader;
Enumeration<URL> configs = null;
Iterator<String> pending = null;
String nextName = null;
private LazyIterator(Class<S> service, ClassLoader loader) {
this.service = service;
this.loader = loader;
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
String fullName = PREFIX + service.getName();
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
return false;
}
pending = parse(service, configs.nextElement());
}
nextName = pending.next();
return true;
}
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service, "Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service, "Provider " + cn + " could not be instantiated", x);
}
throw new Error(); // This cannot happen
}
public boolean hasNext() {
if (acc == null) {
return hasNextService();
} else {
PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
public Boolean run() { return hasNextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public S next() {
return nextService();
}
}
、