概述
SPI
SPI 全称为 Service Provider Interface,是一种服务发现机制。当程序运行调用接口时,会根据配置文件或默认规则信息加载对应的实现类。所以在程序中并没有直接指定使用接口的哪个实现,而是在外部进行装配。
要想了解 Dubbo 的设计与实现,其中 Dubbo SPI 加载机制是必须了解的,在 Dubbo 中有大量功能的实现都是基于 Dubbo SPI 实现解耦,同时也使得 Dubbo 获得如此好的可扩展性。
Java SPI
通过完成一个 Java SPI 的操作来了解它的机制。
- 创建一个 PersonService 接口及 hello 方法
- 创建一个实现类 ChinesePerson
- 创建 META-INF/services 目录,并在该目录下创建一个文件,文件名为 PersonService 的全限定名作为文件名
- 在文件中添加实现类ChinesePerson的全限定名
单元测试
在pom文件中引入junit
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
PersonService接口定义
public interface PersonService {
void hello();
}
ChinesePerson实现类定义
public class ChinesePerson implements PersonService {
@Override
public void hello() {
System.out.println("我是中国人");
}
}
加载 SPI 的实现测试类
public class JavaSPITest {
@Test
public static void main(String[] args) {
ServiceLoader<PersonService> serviceLoader = ServiceLoader.load(PersonService.class);
// 遍历在配置文件中已配置的 AnimalService 的所有实现类
for (PersonService person : serviceLoader) {
person.hello();
}
}
}
执行结果:
我是中国人
Process finished with exit code 0
Dubbo SPI
Dubbo SPI 相较于 Java SPI 更为强大,并且都是由自己实现的一套 SPI 机制。其中主要的改进和优化:
- 相对于 Java SPI 一次性加载所有实现,Dubbo SPI 是按需加载,只加载需要使用的实现类。同时带有缓存支持。
- 更为详细的扩展加载失败信息。
- 增加了对扩展 IOC 和 AOP的支持。
Dubbo SPI 示例
Dubbo SPI 的配置文件放在 META-INF/dubbo 下面,并且实现类的配置方式采用 K-V 的方式,key 为实例化对象传入的参数,value 为扩展点实现类全限定名。例如 ChinesePerson 的配置文件内容:
需要在接口上增加 @SPI 注解,@SPI 中可以指定 key 值
PersonService接口定义如下
@SPI("chinesePerson")
public interface PersonService {
void hello();
}
实现类ChinesePerson定义
public class ChinesePerson implements PersonService {
@Override
public void hello() {
System.out.println("我是中国人");
}
}
DubboSPITest 单元测试类定义
public class DubboSPITest {
@Test
public void spi() {
ExtensionLoader<PersonService> extensionLoader = ExtensionLoader.getExtensionLoader(PersonService.class);
// 获取扩展类实现
PersonService personService = extensionLoader.getExtension("chinesePerson");
personService.hello();
}
}
执行结果如下:
我是中国人
Process finished with exit code 0
SPI 源码分析
获取 ExtensionLoader 实例
ExtensionLoader 类在dubbo的dubbo-common模块中定义,获取 ExtensionLoader 实例是通过上面 getExtensionLoader 方法,具体实现代码:
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
// 检查type 必须为接口类型
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
// 检查type 类型上是否有注解 SPI
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
// 从ConcurrentHashMap 中获取实例
ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
if (loader == null) {
// 创建ExtensionLoader实例,并放入缓存
EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
}
return loader;
}
上面获取扩展类加载器过程主要是检查传入的 type 是否合法,以及从扩展类加载器缓存中是否存在当前类型的接口,如果不存在则添加当前接口至缓存中。ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS
是扩展类加载器的缓存,它是以接口作为 key, 扩展类加载器作为 value 进行缓存。
获取扩展类对象
获取扩展类对象的方法ExtensionLoader#getExtension
,在这里完成扩展对象的缓存及创建工作:
public T getExtension(String name, boolean wrap) {
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
//如果传入的参数为 true ,则获取默认扩展类对象操作
if ("true".equals(name)) {
return getDefaultExtension();
}
//获取扩展对象,Holder 里的 value 属性保存着扩展对象实例
final Holder<Object> holder = getOrCreateHolder(name);
Object instance = holder.get();
//使用双重检查锁
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 创建扩展点对象
instance = createExtension(name, wrap);
holder.set(instance);
}
}
}
return (T) instance;
}
获取 holder 对象是从缓存ConcurrentMap<String, Holder<Object>> cachedInstances
中获取,如果不存在,则以扩展名 key,创建一个 Holder 对象作为 value,设置到扩展对象缓存。
如果是新创建的扩展对象实例,那么 holder.get() 一定是 null ,扩展对象为空时,经过双重检查锁,创建扩展对象。
创建扩展对象
创建扩展对象过程
@SuppressWarnings("unchecked")
private T createExtension(String name, boolean wrap) {
// 从全部扩展类中,获取当前扩展名对应的扩展类
// 读取 META-INF/dubbo/external/
// META-INF/dubbo/internal/
// META-INF/services/
Class<?> clazz = getExtensionClasses().get(name);
if (clazz == null) {
throw findException(name);
}
try {
// 从缓存ConcurrentMap<Class<?>, Object>中获取扩展实例,及设置扩展实例缓存
T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
instance = (T) EXTENSION_INSTANCES.get(clazz);
}
// 向当前实例注入依赖
injectExtension(instance);
if (wrap) {
List<Class<?>> wrapperClassesList = new ArrayList<>();
if (cachedWrapperClasses != null) {
wrapperClassesList.addAll(cachedWrapperClasses);
wrapperClassesList.sort(WrapperComparator.COMPARATOR);
Collections.reverse(wrapperClassesList);
}
if (CollectionUtils.isNotEmpty(wrapperClassesList)) {
for (Class<?> wrapperClass : wrapperClassesList) {
Wrapper wrapper = wrapperClass.getAnnotation(Wrapper.class);
if (wrapper == null
|| (ArrayUtils.contains(wrapper.matches(), name) && !ArrayUtils.contains(wrapper.mismatches(), name))) {
// 创建包装扩展类实例,并向其注入依赖
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
}
}
// 初始化扩展对象
initExtension(instance);
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance (name: " + name + ", class: " +
type + ") couldn't be instantiated: " + t.getMessage(), t);
}
}
获取所有扩展类
private Map<String, Class<?>> getExtensionClasses() {
// 获取普通扩展类缓存
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
// ➕锁
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
// 加载全部扩展类
classes = loadExtensionClasses();
cachedClasses.set(classes);
}
}
}
return classes;
}
loadExtensionClasses 加载全部扩展类
/**
* synchronized in getExtensionClasses
* 读取 META-INF/dubbo/external/
* META-INF/dubbo/internal/
* META-INF/services/
*/
private Map<String, Class<?>> loadExtensionClasses() {
cacheDefaultExtensionName();
Map<String, Class<?>> extensionClasses = new HashMap<>();
for (LoadingStrategy strategy : strategies) {
loadDirectory(extensionClasses, strategy.directory(), type.getName(), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
loadDirectory(extensionClasses, strategy.directory(), type.getName().replace("org.apache", "com.alibaba"), strategy.preferExtensionClassLoader(), strategy.overridden(), strategy.excludedPackages());
}
return extensionClasses;
}
最后
以上就是无心红酒为你收集整理的Dubbo-SPI扩展点加载机制SPIJava SPI的全部内容,希望文章能够帮你解决Dubbo-SPI扩展点加载机制SPIJava SPI所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复