Dubbo的SPI机制介绍以及SPI加载class的方法
发表于:2026-11-01 作者:千家信息网编辑
千家信息网最后更新 2026年11月01日,这篇文章主要讲解了"Dubbo的SPI机制介绍以及SPI加载class的方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以
千家信息网最后更新 2026年11月01日Dubbo的SPI机制介绍以及SPI加载class的方法
这篇文章主要讲解了"Dubbo的SPI机制介绍以及SPI加载class的方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以及SPI加载class的方法"吧!
1、Dubbo的SPI例子
@SPIpublic interface Robot { void sayHello();}public class OptimusPrime implements Robot{ @Override public void sayHello() { System.out.println("Hello, I am Optimus Prime."); }}public class Bumblebee implements Robot{ @Override public void sayHello() { System.out.println("Hello, I am Bumblebee."); }}public class DubboSPITest { @Test public void sayHelloDubbo() throws Exception { ExtensionLoader extensionLoader = ExtensionLoader.getExtensionLoader(Robot.class); Robot optimusPrime = extensionLoader.getExtension("optimusPrime"); optimusPrime.sayHello(); Robot bumblebee = extensionLoader.getExtension("bumblebee"); bumblebee.sayHello(); }} 输出:Hello, I am Optimus Prime.Hello, I am Bumblebee.
2、Dubbo的SPI源码分析
ExtensionLoaderextensionLoader = ExtensionLoader.getExtensionLoader(Robot.class);public static ExtensionLoader getExtensionLoader(Class type) { if (type == null) throw new IllegalArgumentException("Extension type == null"); if (!type.isInterface()) { throw new IllegalArgumentException("Extension type(" + type + ") is not interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type(" + type + ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!"); } // 从缓存中获取与拓展类对应的ExtensionLoader ExtensionLoader loader = (ExtensionLoader ) EXTENSION_LOADERS.get(type); if (loader == null) { // 若缓存未命中,则创建一个新的实例,先简单看下new ExtensionLoader (type) EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader (type)); loader = (ExtensionLoader ) EXTENSION_LOADERS.get(type); } return loader;}
这里的objectFactory创建可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入
private ExtensionLoader(Class> type) { this.type = type; // 这里的type是Robot.class,所以会执行后面的代码,加载并创建SpiExtensionFactory和SpringExtensionFactory, objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());}Robot optimusPrime = extensionLoader.getExtension("optimusPrime");public T getExtension(String name) { if ("true".equals(name)) { return getDefaultExtension(); } // Holder也是用于持有对象的,用作缓存 Holder这里依赖注入可以参考: Dubbo的SPI机制分析3-Dubbo的IOC依赖注入
private T createExtension(String name) { // 从配置文件中加载所有的拓展类,可得到"配置项名称"到"配置类"的映射关系表 // 加载完后根据name获取,得到形如com.alibaba.dubbo.demo.provider.spi.OptimusPrime // 待会重点分析getExtensionClasses(),删去了一些非关键代码 Class> clazz = getExtensionClasses().get(name); try { // 也是尝试先从缓存获取,获取不到通过反射创建一个并放到缓存中 T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } // 依赖注入和cachedWrapperClasses,后面分析 injectExtension(instance); Set> wrapperClasses = cachedWrapperClasses; if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } } private Map> getExtensionClasses() { // 从缓存中获取映射关系表 Map > classes = cachedClasses.get(); // 双重检查 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { // 若缓存中没有,去加载映射关系 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes;}
private Map> loadExtensionClasses() { // 获取SPI注解,这里的type是在调用getExtensionLoader方法时传入的 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { String[] names = NAME_SEPARATOR.split(value); if (names.length > 1) { // 抛异常 } // 获取@SPI注解中的值,并缓存起来,可以关注下,后面会用到 if (names.length == 1) cachedDefaultName = names[0]; } } Map > extensionClasses = new HashMap >(); // 加载指定文件夹下的配置文件,META-INF/services/、META-INF/dubbo/、META-INF/dubbo/internal/ loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // 我是放在这个目录下的 loadDirectory(extensionClasses, DUBBO_DIRECTORY); loadDirectory(extensionClasses, SERVICES_DIRECTORY); return extensionClasses;}
private void loadDirectory(Map> extensionClasses, String dir) { // fileName是META-INF/dubbo/com.alibaba.dubbo.demo.provider.spi.Robot String fileName = dir + type.getName(); try { Enumeration urls; ClassLoader classLoader = findClassLoader(); // 根据文件名加载所有同名文件 if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { // 一个resourceURL就是一个文件 java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL); } } } }
private void loadResource(Map> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), "utf-8")); try { String line; // 按行读取配置内容 while ((line = reader.readLine()) != null) { final int ci = line.indexOf('#'); // 定位#字符,#之后的为注释,跳过 if (ci >= 0) line = line.substring(0, ci); line = line.trim(); if (line.length() > 0) { try { String name = null; // 按等号切割 int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { // 真正的去加载类 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } } } } } }
标注1,这里的可以参考: Dubbo的SPI机制分析2-Adaptive详解
标注2,这里的可以参考: Dubbo的SPI机制分析4-Dubbo通过Wrapper实现AOP
标注3,这里的cachedActivates可以参考: Dubbo的SPI机制分析5-Activate详解
private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class> clazz, String name) throws NoSuchMethodException { // clazz必须是type类型的,否则抛异常 if (!type.isAssignableFrom(clazz)) { } // 判断clazz是否为标注了@Adaptive注解,标注1 if (clazz.isAnnotationPresent(Adaptive.class)) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { // 抛异常,不能有多个标注有@Adaptive注解的类 } } // 判断是否是Wrapper类型,标注2 else if (isWrapperClass(clazz)) { Set > wrappers = cachedWrapperClasses; if (wrappers == null) { cachedWrapperClasses = new ConcurrentHashSet >(); wrappers = cachedWrapperClasses; } wrappers.add(clazz); } // 程序进入此分支,表明clazz是一个普通的拓展类,Robot就是一个普通的拓展类 else { // 检测clazz是否有默认的构造方法,如果没有,则抛出异常 clazz.getConstructor(); if (name == null || name.length() == 0) { name = findAnnotationName(clazz); if (name.length() == 0) { // 抛异常 } } String[] names = NAME_SEPARATOR.split(name); if (names != null && names.length > 0) { // 用于@Activate根据条件激活,标注3 Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(names[0], activate); } for (String n : names) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, n); } Class> c = extensionClasses.get(n); if (c == null) { // 存储名称到class的映射关系,这样就解析好了一行 extensionClasses.put(n, clazz); } else if (c != clazz) { // 抛异常 } } } }}
感谢各位的阅读,以上就是"Dubbo的SPI机制介绍以及SPI加载class的方法"的内容了,经过本文的学习后,相信大家对Dubbo的SPI机制介绍以及SPI加载class的方法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!
机制
分析
缓存
方法
文件
参考
配置
注解
内容
就是
学习
普通
代码
名称
实例
类型
一行
例子
分支
多个
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
互联网科技公司简介
国智互联网科技
美国互联网科技巨头监管
互联网科技金融
互联网金融 科技金融
奇酷互联网科技公司
睿民互联网科技
科技互联网创业计划书
华中科技有哪些互联网大佬
中国互联网公司排名锤子科技
佛山市拉法互联网科技有限公司
中和天地互联网科技有限公司
互联网相关科技技术
福建互联网展示科技馆设计
互联网时代科技型公司的机会
互联网科技手抄报图片
保山互联网科技靠谱吗
恒生科技指数 中概互联网
东风日产互联网科技公司叫什么
现代科技的标志之一的互联网
中国互联网科技股
互联网科技财经
互联网的科技创新
西安互联网科技公司
猫创互联网科技
中化能源互联网科技招聘
江阴互联网智能科技产品进口
长沙互联网科技
互联网企业科技类企业如何做账
科技互联网公司两年变化