Dubbo的SPI机制介绍以及SPI加载class的方法
发表于:2025-02-03 作者:千家信息网编辑
千家信息网最后更新 2025年02月03日,这篇文章主要讲解了"Dubbo的SPI机制介绍以及SPI加载class的方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Dubbo的SPI机制介绍以
千家信息网最后更新 2025年02月03日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 { ExtensionLoaderextensionLoader = 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安全错误
数据库的锁怎样保障安全
445端口网络安全面试题
上位机软件开发
网络安全法与公安
南充创新网络技术有限公司
电脑服务器自动注销
网络安全求职简历
上海共享智能锁软件开发
数据库服务器 负载均衡
数据库试题13的答案
辽事通核酸检测结果全国数据库
网络安全防护概念
医院网络安全指哪些
阡陌客声望数据库
网站上传到服务器如何访问
善诚网络技术有限公司
网络技术对大学生的弊端
浏览器访问服务器不用什么协议
客户数据库技术
联通拨号服务器不响应
天津兆龙软件开发怎么样
软件开发属于哪个税收分类
数据库技术与应用学科
我的世界服务器违规去哪举报
软件开发企业毛利率在多少
85魔兽世界数据库
网络安全的PPT制作
苹果手机截图软件开发
中国药品诚信数据库
网络安全法根据什么来制定的
顺义区网络技术服务便捷