文章目录
前言
SpringBoot中很多地方中使用了SPI扩展机制,那么它是什么实现的呢?本文将从JDK SPI源码开始讲起···
1. SPI扩展机制
SPI(Service Provider Interface),它的优势是接口与实现分离达到解耦、提升程序可扩展性的机制,可以做到轻松插拔。
1.1 JDK SPI
1.1.1 JDK SPI示例代码
public interface SpiProvider {
void print();
}
public class SpiProvider1Impl implements SpiProvider {
@Override
public void print() {
System.out.println("SpiProvider1Impl 11111111111111111");
}
}
public class SpiProvider2Impl implements SpiProvider {
@Override
public void print() {
System.out.println("SpiProvider2Impl 22222222222222222");
}
}
在resources目录下创建META-INF/services/com.analyze.spi.api.SpiProvider文件,内容如下:
com.analyze.spi.api.impl.SpiProvider1Impl
com.analyze.spi.api.impl.SpiProvider2Impl
public class SpiTest {
public static void main(String[] args) {
ServiceLoader<SpiProvider> serviceLoader = ServiceLoader.load(SpiProvider.class);
serviceLoader.forEach(SpiProvider::print);
}
}
执行结果如下:
SpiProvider1Impl 11111111111111111
SpiProvider2Impl 22222222222222222
1.1.2 JDK SPI之ServiceLoader源码
ServiceLoader在
rt.jar
中。
package java.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public final class ServiceLoader<S> implements Iterable<S>{
// 固定配置文件读取目录
private static final String PREFIX = "META-INF/services/";
// 抽象类或接口
private final Class<S> service;
// 用于加载、定位、实例化服务提供者
private final ClassLoader loader;
// 创建ServiceLoader时采取的访问控制上下文
private final AccessControlContext acc;
// 按实例化顺序缓存实现类或子类
private LinkedHashMap<String,S> providers = new LinkedHashMap<>();
// 延时迭代器
private LazyIterator lookupIterator;
/**
* 使用当前线程为这个服务创建一个新的服务提供者加载器。
*
* @param <S> 服务提供者的类型
* @param service 服务提供者(接口或抽象类)
*
* @return 服务提供者加载器
*/
public static <S> ServiceLoader<S> load(Class<S> service) {
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
/**
* 为这个服务创建一个新的服务提供者加载器。
*
* @param <S> 服务提供者的类型
* @param service 服务提供者(接口或抽象类)
* @param loader 服务提供者加载器,若为null则使用ClassLoader.getSystemClassLoader()创建
*
* @return 新的服务提供者加载器
*/
public static <S> ServiceLoader<S> load(Class<S> service,
ClassLoader loader)
{
return new ServiceLoader<>(service, loader);
}
/**
* 服务加载器构造
*/
private ServiceLoader(Class<S> svc, ClassLoader cl) {
service = Objects.requireNonNull(svc, "Service interface cannot be null");
loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;
acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
reload();
}
/**
* 清除服务加载器提供的缓存,以便于当前服务加载器的的重新加载
*/
public void reload() {
// 清除服务加载器提供的缓存
providers.clear();
// 实例化创建懒迭代器
lookupIterator = new LazyIterator(service, loader);
}
// 私有内部类-懒迭代器
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;
}
// 获取下一个实现类或子类
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);
}
}
private boolean hasNextService() {
if (nextName != null) {
return true;
}
if (configs == null) {
try {
// 获取配置文件路径 例如META-INF/services/com.analyze.spi.api.SpiProvider
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();
}
public S next() {
if (acc == null) {
return nextService();
} else {
PrivilegedAction<S> action = new PrivilegedAction<S>() {
public S run() { return nextService(); }
};
return AccessController.doPrivileged(action, acc);
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}
/**
* 解析配置文件中的一行,将该行中的名称添加到名称列表中。
*/
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;
}
/**
* @param service 服务提供者接口或抽象类
* @param u 待解析的配置文件的URL
* @return A 配置文件中子类或实现类成员路径
*/
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();
}
public Iterator<S> iterator() {
return new Iterator<S>() {
// 获取对应实现类或子类缓存
Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();
public boolean hasNext() {
// 判断是否从providers获取到缓存的实现类或子类信息
if (knownProviders.hasNext())
return true;
// 从迭代器中判断是否还存在下一个元素
return lookupIterator.hasNext();
}
public S next() {
// 判断是否从providers获取到缓存的实现类或子类信息
if (knownProviders.hasNext())
return knownProviders.next().getValue();
// 从迭代器中获取下一个元素
return lookupIterator.next();
}
};
}
}
1.1.3 JDK SPI示例代码剖析
1)ServiceLoader.load()
使用当前线程为服务提供者创建一个新的服务加载器。
2)使用forEach
去循环调用对应接口实现时,首先判断是否获取到缓存,为否,则先判断是否存在下一个元素,存在则执行类的装载。
3)读取 META-INF/services下的接口或抽象类对应的配置文件。
4)获得所有能被实例化的类的全限定名,通过Class.forName() 载入类对象,并用 instance() 方法将类实例化。
6)把实例化后的类缓存到providers 对象中然后返回实例对象。
1.1.4 JDK SPI优缺点
优点:
- 解耦,可插拔组件。
缺点:
- 不能按需加载。有些场景下并不想用的实现类或子类,在获取的过程中也会遍历实例化一遍。
- 非线程安全。
1.2 Spring SPI
1.2.1 Spring SPI示例代码
使用上面的接口与实现类,在resources目录下创建META-INF/spring.factories文件,内容如下:
com.analyze.spi.api.SpiProvider=\
com.analyze.spi.api.impl.SpiProvider1Impl,\
com.analyze.spi.api.impl.SpiProvider2Impl
public class SpiTest {
public static void main(String[] args) {
List<SpiProvider> serviceLoader = SpringFactoriesLoader.loadFactories(SpiProvider.class, null);
serviceLoader.forEach(SpiProvider::print);
}
}
执行结果如下:
SpiProvider1Impl 11111111111111111
SpiProvider2Impl 22222222222222222
1.2.2 Spring SPI之SpringFactoriesLoader源码
package org.springframework.core.io.support;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.UrlResource;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public final class SpringFactoriesLoader {
/**
* 读取配置文件的位置
*/
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
private static final Log logger = LogFactory.getLog(SpringFactoriesLoader.class);
/**
* 缓存
* key:给定类型的加载器 value :(key:接口或抽象类全限定名 value:实现类或子类全限定名)
*/
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
private SpringFactoriesLoader() {
}
/**
* 使用给定的类加载器加载并实例化‘META-INF/spring.factories’给定类型的实现类或子类。
* 通过AnnotationAwareOrderComparator进行排序。
* 如果需要自定义实例化的策略,可以通过loadFactoryNames()来获得所有注册工厂的名称。
* @param factoryType 工厂的接口或抽象类
* @param classLoader 加载器(为null则使用默认的SpringFactoriesLoader.class.getClassLoader())
*/
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();
}
// 根据factoryType获取所有实现类或子类的全限定名列表,默认emptyList
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;
}
/**
* 获取给定类型的接口或抽象类的实现类或子类全限定名列表
*
* @param factoryType 工厂的接口或抽象类
* @param classLoader 用于加载的类加载器(为null则使用默认的SpringFactoriesLoader.class.getClassLoader())
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 载入并缓存所有jar下META-INF/spring.factories的配置,并根据工厂的接口或抽象类获取子类或实现类全限定名列表
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
/**
* 载入并缓存所有jar下META-INF/spring.factories的配置
*/
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
// 从通过类加载器从缓存中获取
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
// 获取所有META-INF/spring.factories文件URL
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 遍历获取并存放在result中
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
// 加载Properties配置文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
// 解析Properties配置
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);
}
}
/**
* 通过反射实例化子类或实现类
*
* @param factoryImplementationName 实现类或子类全限定名
* @param factoryType 接口或父类
* @param classLoader 类加载器
*/
@SuppressWarnings("unchecked")
private static <T> T instantiateFactory(String factoryImplementationName, Class<T> factoryType, ClassLoader classLoader) {
try {
// 载入类对象
Class<?> factoryImplementationClass = ClassUtils.forName(factoryImplementationName, classLoader);
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);
}
}
}
1.2.3 Spring SPI示例代码剖析
1)载入并缓存所有jar下META-INF/spring.factories的配置。
2)根据工厂的接口或抽象类全限定名获取子类或实现类全限定名列表。
3)通过子类或实现类全限定名通过反射进行实例化。
4)通过AnnotationAwareOrderComparator进行排序。
结语
关于JDK SPI与Spring SPI就写到这,有什么疑问或问题欢迎在下面留言。