在面向对象编程中,基于开闭原则和解耦的需要,一般建议用接口进行模块之间通信编程,通常情况下调用方模块是不会感知到被调用方模块的内部具体实现
为了实现在模块装配的时候不用在程序里面动态指明,这就需要一种服务发现机制。Java SPI 就是提供了这样一个机制:为某个接口寻找服务实现的机制。这有点类似IOC的思想,将装配的控制权移交到了程序之外。
Java SPI(Service Provider Interface)是 Java 提供的一种轻量级的服务发现机制。它可以让开发者通过约定
的方式,在程序运行时
动态地加载和替换接口的实现,从而提高程序的扩展性和灵活性
Java SPI 实际上是“基于接口的编程+策略模式+配置文件”组合实现的动态加载机制
要使用Java SPI,需要遵循如下约定:
当服务提供者提供了接口的一种具体实现后,在jar包的META-INF/services
目录下创建一个以接口全限定名
为命名的文件,内容为实现类全限定名
接口实现类所在的jar包放在主程序的classpath
中
主程序通过java.util.ServiceLoder
动态装载实现模块,它通过扫描META-INF/services目录下的配置文件找到实现类的全限定名,把类加载到JVM
SPI的实现类必须携带一个无参构造方法
搭建一下场景所需要的环境
现有一个日志框架Logger, 下有一个Logger接口, 以及两个实现类
package com.whitebrocade;
/**
* @author whitebrocade
*/
public interface Logger {
void log(String message);
}
package com.whitebrocade;
/**
* @author whitebrocade
*/
public class ConsoleLogger implements Logger {
@Override
public void log(String message) {
System.out.println("模拟log输出到控制台:" + message);
}
}
package com.whitebrocade;
/**
* @author whitebrocade
*/
public class FileLogger implements Logger {
@Override
public void log(String message) {
System.out.println("模拟log写入文件:" + message);
}
}
需求: 上述两个Logger实现类不足以满足我们的需求, 想自定义拓展
步骤如下
新建自己的类MyLogger以及CustomLogger实现Logger接口
package com.whitebrocade;
/**
* @author whitebrocade
*/
public class MyLogger implements Logger {
@Override
public void log(String message) {
System.out.println("自定义log实现:" + message);
}
}
package com.whitebrocade;
/**
* @author whitebrocade
*/
public class CustomLogger implements Logger {
@Override
public void log(String message) {
System.out.println("自定义log实现2:" + message);
}
}
resource
下新建META-INF/services
, 注意了两个文件夹的名字一定不能变, 规定死的, 约定大于配置(后续会说明)
点击MyLogger, Ctrl + Shiht + Alt + C复制Logger的全限定类名
接口
的全类名在service新建文件, 文件名为就是刚才复制的全类名
(是的, 你没看错, 文件名就是这么奇怪)
在刚才的新建的文件里, 再次将MyLogger和CustomLoggerd的全类名复制下去()
以我的为例子, resource/META-INF/services/com.whitebrocade.Logger文件中的内容如下, 注意一个全类名一行
, 如果后续还有其他的类需要加载, 那么继续按照一下格式添加即可
com.whitebrocade.MyLogger
com.whitebrocade.CustomLogger
新建一个test类用于测试
public class test {
public static void main(String[] args) {
ServiceLoader<Logger> loggerServiceLoader = ServiceLoader.load(Logger.class);
for (Logger logger : loggerServiceLoader) {
logger.log("Hello, Java SPI!");
}
}
}
// 输出结果如下, 说明确实读取到了
自定义log实现:Hello, Java SPI!
自定义log实现2:Hello, Java SPI!
load
方法是通过获取当前线程的 线程上下文类加载器 实例来加载的。Java应用运行的初始线程的上下文类加载器默认是系统类加载器。这里其实 破坏了双亲委派模型,因为Java应用收到类加载的请求时,按照双亲委派模型会向上请求父类加载器完成,这里并没有这么做
下述的分析基于JDK 11的SPI
// ServiceLoader实现了Iterable接口,可以遍历所有的服务实现者
public final class ServiceLoader<S> implements Iterable<S> {
// 表示要被加载的服务的类或接口
private final Class<S> service;
// 这个ClassLoader用来定位,加载,实例化服务提供者
private final ClassLoader loader;
// 访问控制上下文
private final AccessControlContext acc;
// 用于迭代器操作的lazy-lookup迭代器
private Iterator<Provider<S>> lookupIterator1;
private final List<S> instantiatedProviders = new ArrayList<>();
}
newLookupIterator()
: 初始迭代器hasNext()
: 判断是否有下一个元素next()
: 获取下一个元素public Iterator<S> iterator() {
// 首次进来初始化
if (lookupIterator1 == null) {
lookupIterator1 = newLookupIterator();
}
return new Iterator<S>() {
// record reload count
final int expectedReloadCount = ServiceLoader.this.reloadCount;
// index into the cached providers list
int index;
/**
* Throws ConcurrentModificationException if the list of cached
* providers has been cleared by reload.
*/
private void checkReloadCount() {
if (ServiceLoader.this.reloadCount != expectedReloadCount)
throw new ConcurrentModificationException();
}
@Override
public boolean hasNext() {
checkReloadCount();
if (index < instantiatedProviders.size())
return true;
// 调用的是java.util.Iterator#hasNext
return lookupIterator1.hasNext();
}
@Override
public S next() {
checkReloadCount();
S next;
if (index < instantiatedProviders.size()) {
next = instantiatedProviders.get(index);
} else {
next = lookupIterator1.next().get();
instantiatedProviders.add(next);
}
index++;
return next;
}
};
}
private Iterator<Provider<S>> newLookupIterator() {
assert layer == null || loader == null;
if (layer != null) {
return new LayerLookupIterator<>();
} else {
Iterator<Provider<S>> first = new ModuleServicesLookupIterator<>();
Iterator<Provider<S>> second = new LazyClassPathLookupIterator<>();
return new Iterator<Provider<S>>() {
@Override
public boolean hasNext() {
// second.hasNext()调用的是java.util.ServiceLoader.LazyClassPathLookupIterator#hasNext
return (first.hasNext() || second.hasNext());
}
@Override
public Provider<S> next() {
if (first.hasNext()) {
return first.next();
} else if (second.hasNext()) {
// 进入java.util.ServiceLoader.LazyClassPathLookupIterator#next
return second.next();
} else {
throw new NoSuchElementException();
}
}
};
}
}
这个方法说明了
META-INF/services/
下全类名
// java.util.ServiceLoader.LazyClassPathLookupIterator#nextProviderClass
private final class LazyClassPathLookupIterator<T>
implements Iterator<Provider<T>>
{
// 查找配置文件的目录
// PREFIX说明了为什么只能放在META-INF/services/目录
static final String PREFIX = "META-INF/services/";
// 省略其他代码...
private Class<?> nextProviderClass() {
// config初始化
if (configs == null) {
try {
// 路径全名称为: "META-INF/services/" + 类的名称
// service.getName()说明了为什么一定要全路径命名
String fullName = PREFIX + service.getName();
// loader == null, 说明是bootstrap类加载器
if (loader == null) {
// 从classpath中加载指定的文件
configs = ClassLoader.getSystemResources(fullName);
} else if (loader == ClassLoaders.platformClassLoader()) {
if (BootLoader.hasClassPath()) {
configs = BootLoader.findResources(fullName);
} else {
configs = Collections.emptyEnumeration();
}
} else {
// 通过名字加载所有文件资源
configs = loader.getResources(fullName);
}
} catch (IOException x) {
fail(service, "Error locating configuration files", x);
}
}
// 遍历所有的资源,pending用于存放加载到的实现类
while ((pending == null) || !pending.hasNext()) {
if (!configs.hasMoreElements()) {
// 遍历完所有的文件了,直接返回
return null;
}
// parse方法主要调用了parseLine,功能:
// 1. 分析每个PREFIX + service.getName() 目录下面的所有文件
// 2. 判断每个文件是否是合法的java类的全限定名称,如果是就add到pending变量中
pending = parse(configs.nextElement());
}
// 除了第一次进来,后面每次调用都是直接到这一步了
// cn即className-全类名
String cn = pending.next();
try {
// 通过Class.forName返回配置文件的中的Class对象
return Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service, "Provider " + cn + " not found");
return null;
}
}
// 省略其他代码...
}
// java.util.ServiceLoader.LazyClassPathLookupIterator#hasNextService
private final class LazyClassPathLookupIterator<T>
implements Iterator<Provider<T>>
{
// 省略其它代码...
@SuppressWarnings("unchecked")
private boolean hasNextService() {
while (nextProvider == null && nextError == null) {
try {
// 调用nextProviderClass()获取Class对象
Class<?> clazz = nextProviderClass();
if (clazz == null)
return false;
if (clazz.getModule().isNamed()) {
// ignore class if in named module
continue;
}
// 判断clazz对象是不是service的实现类或者子类
// 如果是, 那么就调用实现类初始化
// 以我们程序为例子
// clazz就是class com.whitebrocade.MyLogger
// type就是class com.whitebrocade.Logger
// cotor就是public com.whitebrocade.MyLogger()
if (service.isAssignableFrom(clazz)) {
// 通过无参构造器初始化clazz
Class<? extends S> type = (Class<? extends S>) clazz;
Constructor<? extends S> ctor
= (Constructor<? extends S>)getConstructor(clazz);
ProviderImpl<S> p = new ProviderImpl<S>(service, type, ctor, acc);
// 将实现类赋值给nextProvider
// 即Logger logger = new MyLogger()意思
nextProvider = (ProviderImpl<T>) p;
} else {
fail(service, clazz.getName() + " not a subtype");
}
} catch (ServiceConfigurationError e) {
nextError = e;
}
}
return true;
}
// 省略其它代码...
}
// java.util.ServiceLoader.LazyClassPathLookupIterator#nextService
private final class LazyClassPathLookupIterator<T>
implements Iterator<Provider<T>> {
// 省略其它代码...
private Provider<T> nextService() {
// 校验一下
if (!hasNextService())
throw new NoSuchElementException();
//
Provider<T> provider = nextProvider;
if (provider != null) {
nextProvider = null;
// 将provider返回, provider的赋值动作在hasNextService()
return provider;
} else {
ServiceConfigurationError e = nextError;
assert e != null;
nextError = null;
throw e;
}
}
// 省略其它代码...
}
// java.util.ServiceLoader.LazyClassPathLookupIterator#next
private final class LazyClassPathLookupIterator<T>
implements Iterator<Provider<T>> {
// 省略其它代码...
@Override
public Provider<T> next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<Provider<T>> action = new PrivilegedAction<>() {
public Provider<T> run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
// 省略其它代码...
}
public Iterator<S> iterator() {
// create lookup iterator if needed
if (lookupIterator1 == null) {
lookupIterator1 = newLookupIterator();
}
return new Iterator<S>() {
// record reload count
final int expectedReloadCount = ServiceLoader.this.reloadCount;
// index into the cached providers list
int index;
/**
* Throws ConcurrentModificationException if the list of cached
* providers has been cleared by reload.
*/
private void checkReloadCount() {
if (ServiceLoader.this.reloadCount != expectedReloadCount)
throw new ConcurrentModificationException();
}
@Override
public boolean hasNext() {
checkReloadCount();
if (index < instantiatedProviders.size())
return true;
return lookupIterator1.hasNext();
}
@Override
public S next() {
checkReloadCount();
S next;
if (index < instantiatedProviders.size()) {
next = instantiatedProviders.get(index);
} else {
next = lookupIterator1.next().get();
instantiatedProviders.add(next);
}
index++;
return next;
}
};
}
文字描述
当首次使用迭代
后续流程
Spring SPI对 Java SPI 进行了封装增强。我们只需要在resource目录下META-INF/spring.factories
中配置接口实现类名,即可通过服务发现机制,在运行时加载接口的实现类
META-INF/spring.factories
spring.factories
, 不可以更改接口全类名=实现类全类名
, 多个实现类的化, Value使用逗号间隔先让我们整一个Spring SPI的Demo玩一下, 把之间的JDK SPI改造成Spring SPI
resource目录下的META-INF
下新建spring.factories
文件, 文件中输入下述内容
com.whitebrocade.Logger=com.whitebrocade.MyLogger, com.whitebrocade.CustomLogger
新建一个测试方法
@Test
public void test() {
List<Logger> loggerList = SpringFactoriesLoader.loadFactories(Logger.class, this.getClass().getClassLoader());
for (Logger logger : loggerList) {
logger.log("Hello, Spring SPI!");
}
}
// 输出结果如下
// 自定义log实现:Hello, Spring SPI!
// 自定义log实现2:Hello, Spring SPI!
public static <T> List<T> loadFactories(Class<T> factoryType, @Nullable ClassLoader classLoader) {
Assert.notNull(factoryType, "'factoryType' must not be null");
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
// 确定类加载器
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
// 解析和加载MEAT-INF下的文件
List<String> factoryImplementationNames = loadFactoryNames(factoryType, classLoaderToUse);
if (logger.isTraceEnabled()) {
logger.trace("Loaded [" + factoryType.getName() + "] names: " + factoryImplementationNames);
}
List<T> result = new ArrayList<>(factoryImplementationNames.size());
for (String factoryImplementationName : factoryImplementationNames) {
result.add(instantiateFactory(factoryImplementationName, factoryType, classLoaderToUse));
}
AnnotationAwareOrderComparator.sort(result);
return result;
}
// org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
// 使用给定的类加载器从 “meta-inf/spring.factories” 加载给定类型的工厂实现的完全限定类名
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
// 判断factoryImplementationClass对象是不是factoryType的实现类或者子类
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
// factoryImplementationClass是factoryType的子类或实现类
// 如果是, 那么就调用factoryImplementationClass的无参构造器初始化对象, 并转成factoryType类型
// 这里的factoryType相当于接口, factoryImplementationClass相当于实现类
// 对于我们的程序
// factoryType就是interface com.whitebrocade.Logger
// factoryImplementationClass就是class com.whitebrocade.MyLogger
if (!factoryType.isAssignableFrom(factoryImplementationClass)) {
throw new IllegalArgumentException(
"Class [" + factoryImplementationName + "] is not assignable to factory type [" + factoryType.getName() + "]");
}
return (T) ReflectionUtils.accessibleConstructor(factoryImplementationClass).newInstance();
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Unable to instantiate factory class [" + factoryImplementationName + "] for factory type [" + factoryType.getName() + "]",
ex);
}
}
不推荐, 主要原因还是我们可以使用更优雅的方式来替代SPI
机制,比如:
基本都会使用到SPI, 现在的诸多框架及工具就是使用SPI来实现的,引入了SPI机制后,服务接口与服务实现就会达成分离的状态,可以实现解耦以及可扩展机制
Java实现的SPI版本相对比较粗糙和暴力,导致它会把所有接口实现类全部实例化一遍,所以还有框架会对Java的SPI进行封装和优化
java菜鸟到大佬——全网最全SPI机制讲解
理解的Java中SPI机制
深入理解 Java 中 SPI 机制
一文搞懂Spring的SPI机制(详解与运用实战)
springboot-starter中的SPI 机制
深入剖析Spring Boot 的SPI机制
「一探究竟」Java SPI机制