前言
前面我已经介绍【面试系列】详细拆解Java、Spring、Dubbo三者SPI机制的原理,当已经有了合适的实现,shenyu自身的SPI和上面的有啥区别,值得玩味。
什么是SPI
SPI
就是Service Provider Interface
,直译"服务提供方接口",是一种动态的服务发现机制,可以基于接口运行时动态加载接口的实现类(也就是接口编程 + 策略模式 + 配置文件的一种开发模式)。最常见的就是JDK
内置的数据库驱动接口java.sql.Driver
,不同的厂商可以对该接口完成不同的实现,例如MySQL
(MySQL
驱动包中的com.mysql.jdbc.Driver
)、PostgreSQL
(PostgreSQL
驱动包中的org.postgresql.Driver
)等等。
ShenYu的SPI
Demo
JdbcSPI jdbcSPI = ExtensionLoader.getExtensionLoader(JdbcSPI.class).getJoin("mysql");
源码
ExtensionLoader#getExtensionLoader(java.lang.Class<T>)
,判断类是否是接口,是否存在@SPI
注解。
public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz) {
return getExtensionLoader(clazz, ExtensionLoader.class.getClassLoader());
}
public static <T> ExtensionLoader<T> getExtensionLoader(final Class<T> clazz, final ClassLoader cl) {
Objects.requireNonNull(clazz, "extension clazz is null");
if (!clazz.isInterface()) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") is not interface!");
}
if (!clazz.isAnnotationPresent(SPI.class)) {
throw new IllegalArgumentException("extension clazz (" + clazz + ") without @" + SPI.class + " Annotation");
}
ExtensionLoader<T> extensionLoader = (ExtensionLoader<T>) LOADERS.get(clazz);
if (Objects.nonNull(extensionLoader)) {
return extensionLoader;
}
LOADERS.putIfAbsent(clazz, new ExtensionLoader<>(clazz, cl));
return (ExtensionLoader<T>) LOADERS.get(clazz);
}
ExtensionLoader
,构造方法。获取ExtensionFactory
。
private ExtensionLoader(final Class<T> clazz, final ClassLoader cl) {
this.clazz = clazz;
this.classLoader = cl;
if (!Objects.equals(clazz, ExtensionFactory.class)) {
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getExtensionClassesEntity();
}
}
ExtensionLoader#getExtensionClassesEntity
,获取本地缓存,本地缓存不存在,加载类信息。
private Map<String, ClassEntity> getExtensionClassesEntity() {
Map<String, ClassEntity> classes = cachedClasses.getValue();
if (Objects.isNull(classes)) {
synchronized (cachedClasses) {
classes = cachedClasses.getValue();
if (Objects.isNull(classes)) {
classes = loadExtensionClass();
cachedClasses.setValue(classes);
cachedClasses.setOrder(0);
}
}
}
return classes;
}
ExtensionLoader#loadExtensionClass
,加载类信息主要是加载指定目录下面的文件
private Map<String, ClassEntity> loadExtensionClass() {
SPI annotation = clazz.getAnnotation(SPI.class);
if (Objects.nonNull(annotation)) {
String value = annotation.value();
if (StringUtils.isNotBlank(value)) {
cachedDefaultName = value;
}
}
Map<String, ClassEntity> classes = new HashMap<>(16);
loadDirectory(classes);
return classes;
}
ExtensionLoader#loadDirectory
,加载目录下的文件
private void loadDirectory(final Map<String, ClassEntity> classes) {
String fileName = SHENYU_DIRECTORY + clazz.getName();
try {
Enumeration<URL> urls = Objects.nonNull(this.classLoader) ? classLoader.getResources(fileName)
: ClassLoader.getSystemResources(fileName);
if (Objects.nonNull(urls)) {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
loadResources(classes, url);
}
}
} catch (IOException t) {
LOG.error("load extension class error {}", fileName, t);
}
}
ExtensionLoader#loadResources
,加载资源文件
private void loadResources(final Map<String, ClassEntity> classes, final URL url) throws IOException {
try (InputStream inputStream = url.openStream()) {
Properties properties = new Properties();
properties.load(inputStream);
properties.forEach((k, v) -> {
String name = (String) k;
String classPath = (String) v;
if (StringUtils.isNotBlank(name) && StringUtils.isNotBlank(classPath)) {
try {
loadClass(classes, name, classPath);
} catch (ClassNotFoundException e) {
throw new IllegalStateException("load extension resources error", e);
}
}
});
} catch (IOException e) {
throw new IllegalStateException("load extension resources error", e);
}
}
ExtensionLoader#loadClass
,判断是否是实现类,是否存在Join
注解,满足条件,封装成ClassEntity
,添加到classes
中。
private void loadClass(final Map<String, ClassEntity> classes,
final String name, final String classPath) throws ClassNotFoundException {
Class<?> subClass = Objects.nonNull(this.classLoader) ? Class.forName(classPath, true, this.classLoader) : Class.forName(classPath);
if (!clazz.isAssignableFrom(subClass)) {
throw new IllegalStateException("load extension resources error," + subClass + " subtype is not of " + clazz);
}
if (!subClass.isAnnotationPresent(Join.class)) {
throw new IllegalStateException("load extension resources error," + subClass + " without @" + Join.class + " annotation");
}
ClassEntity oldClassEntity = classes.get(name);
if (Objects.isNull(oldClassEntity)) {
Join joinAnnotation = subClass.getAnnotation(Join.class);
ClassEntity classEntity = new ClassEntity(name, joinAnnotation.order(), subClass, joinAnnotation.isSingleton());
classes.put(name, classEntity);
} else if (!Objects.equals(oldClassEntity.getClazz(), subClass)) {
throw new IllegalStateException("load extension resources error,Duplicate class " + clazz.getName() + " name "
+ name + " on " + oldClassEntity.getClazz().getName() + " or " + subClass.getName());
}
}
ExtensionLoader#getJoin
,创建对应的实例。
public T getJoin(final String name) {
if (StringUtils.isBlank(name)) {
throw new NullPointerException("get join name is null");
}
Holder<Object> objectHolder = cachedInstances.get(name);
if (Objects.isNull(objectHolder)) {
cachedInstances.putIfAbsent(name, new Holder<>());
objectHolder = cachedInstances.get(name);
}
Object value = objectHolder.getValue();
if (Objects.isNull(value)) {
synchronized (cachedInstances) {
value = objectHolder.getValue();
if (Objects.isNull(value)) {
createExtension(name, objectHolder);
value = objectHolder.getValue();
if (!objectHolder.isSingleton()) {
Holder<Object> removeObj = cachedInstances.remove(name);
removeObj = null;
}
}
}
}
return (T) value;
}
ExtensionLoader#createExtension
,根据接口信息去找到配置文件,从配置文件中读取实现类。
private void createExtension(final String name, final Holder<Object> holder) {
ClassEntity classEntity = getExtensionClassesEntity().get(name);
if (Objects.isNull(classEntity)) {
throw new IllegalArgumentException(name + "name is error");
}
Class<?> aClass = classEntity.getClazz();
Object o = joinInstances.get(aClass);
if (Objects.isNull(o)) {
try {
if (classEntity.isSingleton()) {
joinInstances.putIfAbsent(aClass, aClass.newInstance());
o = joinInstances.get(aClass);
} else {
o = aClass.newInstance();
}
} catch (InstantiationException | IllegalAccessException e) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: "
+ aClass + ") could not be instantiated: " + e.getMessage(), e);
}
}
holder.setOrder(classEntity.getOrder());
holder.setValue(o);
holder.setSingleton(classEntity.isSingleton());
}
配置文件如下
mysql=org.apache.shenyu.spi.fixture.MysqlSPI
oracle=org.apache.shenyu.spi.fixture.OracleSPI
canNotInstantiated=org.apache.shenyu.spi.fixture.CanNotInstantiatedSPI
总结一下
Shenyu
的spi,更多像是dubbo的spi简化版。springboot是key=a,b,c
,不涉及对应关系。dubbo是类名作为文件名,key获取实现类的主键。相比于dubbo,简化了adaptive等功能。