【Dubbo源码】SPI机制源码解析,2024年最新java信息管理系统设计步骤

private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + “internal/”;

//分隔符

private static final Pattern NAME_SEPARATOR = Pattern.compile(“\s*[,]+\s*”);

//存放所有需要扩展的接口类名,和对应的ExtensionLoader扩展加载器

private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();

private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap

  • 前面三个文件夹路径SERVICES_DIRECTORYDUBBO_DIRECTORYDUBBO_INTERNAL_DIRECTORY本质上没有区别,因为这三个路径都会被dubbo扫描一遍,把这些文件夹下面的文件全部加载到内存中;

  • ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS这个Map是用来存放所有的SPI的管理的ClassExtensionLoader扩展器的;

比如dubbo内置的

在这里插入图片描述

上面的这些文件名都是一个interface的全类名路径;那么我们的EXTENSION_LOADERS中的key就对应这些interface,value就对应一个单独的ExtensionLoader扩展器;

  • ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES

TODO…

静态方法

//是否有SPI注解

private static boolean withExtensionAnnotation(Class type) {

return type.isAnnotationPresent(SPI.class);

}

@SuppressWarnings(“unchecked”)

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 loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);

if (loader == null) {

EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type));

loader = (ExtensionLoader) EXTENSION_LOADERS.get(type);

}

return loader;

}

//获取ClassLoader

private static ClassLoader findClassLoader() {

return ExtensionLoader.class.getClassLoader();

}

  • 主要看上面的getExtensionLoader方法,这个是非常关键的一个方法,因为dubbo想要获取对应Class的一个实例,那么需要先获取这个ClassExtensionLoader扩展加载器,这个方法就是对外提供的一个入口;

  • 这个Class必须是一个interface,必须打上了SPI的注解

  • 我们发现获取这个 ExtensionLoader扩展加载器 是从全局静态变量EXTENSION_LOADERS获取的;但是一开始没有的情况,需要先实例化一个 ExtensionLoader扩展加载器 出来 new ExtensionLoader<T>(type) ;

为了方便分析源码,我们一起启动服务,开启Debug;Dubbo最先调用ExtensionLoader的是

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

在这里插入图片描述

new ExtensionLoader(type) 实例化扩展加载器


以首先被加载的Protocol为例

private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

最终是执行了new ExtensionLoader<T>(type)的方法;并且保存到了静态属性EXTENSION_LOADERS中;

我们看看是怎么实例化的

private ExtensionLoader(Class<?> type) {

this.type = type;

objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

}

当前的type=interface com.alibaba.dubbo.rpc.Protocol; 那么这个ExtensionFactory objectFactory;属性又是什么呢?

TODO…

那么这里要先去执行

ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());

这个时候的type=interface com.alibaba.dubbo.common.extension.ExtensionFactory

ExtensionFactory也new了一个ExtensionLoader之后,然后去调用方法getAdaptiveExtension(); 这个方法的作用是

获取自适应扩展

public T getAdaptiveExtension() {

Object instance = cachedAdaptiveInstance.get();

if (instance == null) {

if (createAdaptiveInstanceError == null) {

synchronized (cachedAdaptiveInstance) {

instance = cachedAdaptiveInstance.get();

if (instance == null) {

try {

instance = createAdaptiveExtension();

cachedAdaptiveInstance.set(instance);

} catch (Throwable t) {

createAdaptiveInstanceError = t;

throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);

}

}

}

} else {

throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);

}

}

return (T) instance;

}

  • cachedAdaptiveInstance缓存了自适应扩展的实例类;

  • createAdaptiveExtension()方法创建了自适应扩展的实例,并存放入cachedAdaptiveInstance

  • 如果这个自适应扩展实例存在的话就直接返回了

那么,是如何创建 自适应扩展实例的呢?

如何创建自适应扩展实例

@SuppressWarnings(“unchecked”)

private T createAdaptiveExtension() {

try {

return injectExtension((T) getAdaptiveExtensionClass().newInstance());

} catch (Exception e) {

throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);

}

}

简而言之

  • 先获取自适应扩展Class

  • 然后调用ClassnewInstance()方法来实例化对象

  • 生成的对象,可能还有一些属性要注入,所以执行了方法injectExtension依赖注入;

那么问题又来了

如何获取自适应扩展的Class

实例化之后,如何依赖注入?

如何获取自适应扩展的Class

获取自适应扩展的Class, 得先加载文件夹下面的文件啊,自适应扩展也是SPI机制中管理的其中一个比较特殊的类而已;

private Class<?> getAdaptiveExtensionClass() {

getExtensionClasses();

if (cachedAdaptiveClass != null) {

return cachedAdaptiveClass;

}

return cachedAdaptiveClass = createAdaptiveExtensionClass();

}

  • 加载当前type中所有的扩展类,(加载的具体详情请看下面)

如果扩展类中有带有注解@Adaptive,说明是自适应扩展类,直接返回

一个type有且只有一个自适应扩展类

  • 如果当前type中所有的扩展类中没有找到带有注解@Adaptive自适应扩展类的话,就会主动去创建一个自适应扩展类
如何自动创建自适应扩展类

private Class<?> createAdaptiveExtensionClass() {

String code = createAdaptiveExtensionClassCode();

ClassLoader classLoader = findClassLoader();

com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();

return compiler.compile(code, classLoader);

}

如果没有自适应扩展类,则dubbo会自动生成一个;

先拼接类

  • 查询当前type的所有方法中是否有注解@Adaptive(是方法中的注解),如果一个都没有的话,那么就会抛出异常;

  • 遍历每一个method,如果方法中没有注解,则该方法拼接出来的就是直接抛异常说不支持该方法;

  • 拼接的过程太繁琐了,直接给一个拼接之后的例子吧

Protocol为例子

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {

public void destroy() {

throw new UnsupportedOperationException(“method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!”);

}

public int getDefaultPort() {

throw new UnsupportedOperationException(“method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!”);

}

public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1)

throws com.alibaba.dubbo.rpc.RpcException {

if (arg1 == null) throw new IllegalArgumentException(“url == null”);

com.alibaba.dubbo.common.URL url = arg1;

String extName = ( url.getProtocol() == null ? “dubbo” : url.getProtocol() );

if(extName == null)

throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) " +

“name from url(” + url.toString() + “) use keys([protocol])”);

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)

ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.refer(arg0, arg1);

}

public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {

if (arg0 == null) throw new IllegalArgumentException(“com.alibaba.dubbo.rpc.Invoker argument == null”);

if (arg0.getUrl() == null) throw new IllegalArgumentException(“com.alibaba.dubbo.rpc.Invoker argument getUrl() == null”);

com.alibaba.dubbo.common.URL url = arg0.getUrl();

String extName = ( url.getProtocol() == null ? “dubbo” : url.getProtocol() );

if(extName == null) throw new IllegalStateException(“Fail to get extension(com.alibaba.dubbo.rpc.Protocol)” +

" name from url(" + url.toString() + “) use keys([protocol])”);

com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)

ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);

return extension.export(arg0);

}

}

  • 拼接这个类最主要的地方在ExtensionLoader.getExtensionLoader(T).getExtension(extName);; 这个extName究竟是多少,拼接的逻辑在下面,那个getNameCode就是最终的extName在这里插入图片描述

  • 拼接完成这个类之后,然后选择某个Compiler来生产字节码;选择Compiler也是通过SPI选择的;

实例化之后,如何依赖注入?

上面已经分析完如何获取自适应扩展类; 实例完了之后还没有完成;因为扩展类 里面可能还有设置一些属性;所有还有一个依赖注入的过程

private T injectExtension(T instance) {

try {

if (objectFactory != null) {

for (Method method : instance.getClass().getMethods()) {

if (method.getName().startsWith(“set”)

&& method.getParameterTypes().length == 1

&& Modifier.isPublic(method.getModifiers())) {

Class<?> pt = method.getParameterTypes()[0];

try {

String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : “”;

Object object = objectFactory.getExtension(pt, property);

if (object != null) {

method.invoke(instance, object);

}

} catch (Exception e) {

logger.error("fail to inject via method " + method.getName()

  • " of interface " + type.getName() + ": " + e.getMessage(), e);

}

}

}

}

} catch (Exception e) {

logger.error(e.getMessage(), e);

}

return instance;

}

  • 如果objectFactory==null的情况就直接返回了,不需要依赖注入;什么情况下这个值是null?,只有type=com.alibaba.dubbo.common.extension.ExtensionFactory;情况下这个才直接返回了;

  • 如果实例中的方法是 set 开头的并且只有一个入参,并且是public权限的,就可以依赖注入了

  • 那么注入的属性从哪里来呢?

依赖注入的属性从哪里来

我们可以看到是从Object object = objectFactory.getExtension(pt, property);得到的注入属性,然后执行method.invoke(instance, object);进行注入;

从扩展工厂类中获取 扩展实例

这个objectFactory=ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension();

dubbo中的自适应扩展类AdaptiveExtensionFactory

@Adaptive

public class AdaptiveExtensionFactory implements ExtensionFactory {

private final List factories;

public AdaptiveExtensionFactory() {

ExtensionLoader loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);

List list = new ArrayList();

for (String name : loader.getSupportedExtensions()) {

list.add(loader.getExtension(name));

}

factories = Collections.unmodifiableList(list);

}

@Override

public T getExtension(Class type, String name) {

for (ExtensionFactory factory : factories) {

T extension = factory.getExtension(type, name);

if (extension != null) {

return extension;

}

}

return null;

}

}

  • loader.getSupportedExtensions()获取到的是除了自适应类包装类之外的扩展类;那么这个方法得到的名字有两个,①SpiExtensionFactory ②SpringExtensionFactory, 拿到的只是名字而已,那么还要通过loader.getExtension(name)来拿到对应的实例对象! 具体的创建实例对象细节看后面

在这里的情况,那么factories中就有两个扩展实例

1.SpiExtensionFactory SPI的扩展工厂

2.SpringExtensionFactory Spirng的扩展工厂

  • 那么获取属性的时候我们看到调用了Object object = objectFactory.getExtension(pt, property); 执行的方法就是在这里插入图片描述

可以看到遍历执行SpiExtensionFactorySpringExtensionFactory两个扩展类的getExtension方法;

例如SpiExtensionFactory;

在这里插入图片描述

所以这个扩展工厂类,我们也可以写自己的扩展工厂类来生成对应的对象来依赖注入对象!

加载当前Type中所有的扩展类

加载扩展类比较重要,所以我单独拉出来细说

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;

}

// synchronized in getExtensionClasses

private Map<String, Class<?>> loadExtensionClasses() {

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) {

throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()

  • ": " + Arrays.toString(names));

}

if (names.length == 1) cachedDefaultName = names[0];

}

}

Map<String, Class<?>> extensionClasses = new HashMap

loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);

loadDirectory(extensionClasses, DUBBO_DIRECTORY);

loadDirectory(extensionClasses, SERVICES_DIRECTORY);

return extensionClasses;

}

  • 如果cachedClasses不为空直接返回;说明已经加载过了,这个就是用来保存当前Class中的所有扩展类名;

在这里插入图片描述

cachedClasses的key是左边值,value是右边对应的Class

  • 如果还没有加载过,则开始加载

  • 如果当前的type上的@SPI有默认值,例如@SPI("dubbo"),则将其设置到属性cachedDefaultName中;

  • 加载三个文件夹DUBBO_INTERNAL_DIRECTORYDUBBO_DIRECTORYSERVICES_DIRECTORY下面的对应type文件中的具体实现类;

加载文件中的具体实现类

private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {

if (!type.isAssignableFrom(clazz)) {

throw new IllegalStateException("Error when load extension class(interface: " +

type + ", class line: " + clazz.getName() + "), class "

  • clazz.getName() + “is not subtype of interface.”);

}

if (clazz.isAnnotationPresent(Adaptive.class)) {

if (cachedAdaptiveClass == null) {

cachedAdaptiveClass = clazz;

} else if (!cachedAdaptiveClass.equals(clazz)) {

throw new IllegalStateException("More than 1 adaptive class found: "

  • cachedAdaptiveClass.getClass().getName()

  • ", " + clazz.getClass().getName());

}

} else if (isWrapperClass(clazz)) {

Set<Class<?>> wrappers = cachedWrapperClasses;

if (wrappers == null) {

cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();

wrappers = cachedWrapperClasses;

}

wrappers.add(clazz);

} else {

clazz.getConstructor();

if (name == null || name.length() == 0) {

name = findAnnotationName(clazz);

if (name == null || name.length() == 0) {

if (clazz.getSimpleName().length() > type.getSimpleName().length()

&& clazz.getSimpleName().endsWith(type.getSimpleName())) {

name = clazz.getSimpleName().substring(0, clazz.getSimpleName().length() - type.getSimpleName().length()).toLowerCase();

} else {

throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL);

}

}

}

String[] names = NAME_SEPARATOR.split(name);

if (names != null && names.length > 0) {

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) {

extensionClasses.put(n, clazz);

} else if (c != clazz) {

throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());

}

}

}

}

}

  • 文件中的扩展类必须是当前type的实现类

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
img
img
img

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
img

c = extensionClasses.get(n);

if (c == null) {

extensionClasses.put(n, clazz);

} else if (c != clazz) {

throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());

}

}

}

}

}

  • 文件中的扩展类必须是当前type的实现类

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
[外链图片转存中…(img-pFU6AOmy-1712542367793)]
[外链图片转存中…(img-hutSSpgm-1712542367794)]
[外链图片转存中…(img-Ud3wYmZ6-1712542367794)]

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-nbqbqGVg-1712542367794)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值