在这里讲解GitHub上面最新的版本的Dubbo4.3.16版本。
1.SPI机制介绍
SPI机制,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。因此,很容易的通过 SPI 机制为我们的程序提供拓展功能。对于Java的原生SPI机制在这里不多做讲解,可以在网上搜索到很多讲解的。这里通过一个例子简单说明Java的SPI机制。
2.JAVA的SPI机制

1.创建一个基础的接口类
1
2
3
4public interface JavaSpiTestService { String test(); }
2.对上面创建地接口类进行实现
1
2
3
4
5
6
7public class JavaSpiTestServiceImpl implements JavaSpiTestService { @Override public String test() { return "测试"; } }
3.在resource目录下创建一个META-INF并在其下创建一个services目录,然后用上面创建的接口类的相对路径来创建一个文件名,文件内容是对应的实现类的相对路径
1
2othertest.demo.impl.JavaSpiTestServiceImpl
4.进行测试
1
2
3
4
5
6
7
8
9public class MainTestClass { public static void main(String[] args) { ServiceLoader<JavaSpiTestService> load = ServiceLoader.load(JavaSpiTestService.class); for (JavaSpiTestService testService : load) { System.out.println(testService.test());; } } }
运行结果为
1
2测试
3.Dubbo的SPI机制
Dubbo的SPI机制是重新实现的一套SPI机制。所有的逻辑都被封装在了ExtensionLoader类中。Dubbo SPI 所需的配置文件需放置在 META-INF/dubbo 路径下,配置内容如下。
1.目录结构

可以发现目录结构和Java的SPI机制结构相似。
2.自定义接口类
&mesp;在测试自定义的接口的时候,需要在类上加上@SPI这个注解
1
2
3
4
5@SPI public interface DubboSpiTestService { String sayHello(); }
3.对上面的自定义实现类进行实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14public class DubboSpiTestServiceImplOne implements DubboSpiTestService { @Override public String sayHello() { return "我是Dubbo的SPI机制的第一个实现类"; } } ----------- public class DubboSpiTestServiceImplTwo implements DubboSpiTestService { @Override public String sayHello() { return "我是Dubbo的SPI机制的第二个实现"; } }
4.配置文件的内容
Dubbo的SPI机制的配置未见的格式是键值对的形式,与Java的Spi配置文件不同
1
2
3dubboOne=othertest.demo.impl.DubboSpiTestServiceImplOne dubboTwo=othertest.demo.impl.DubboSpiTestServiceImplTwo
5.测试
1
2
3
4
5
6
7
8
9
10public class MainTestClass { public static void main(String[] args) { ExtensionLoader<DubboSpiTestService> extensionLoader = ExtensionLoader.getExtensionLoader(DubboSpiTestService.class); DubboSpiTestService dubboOne = extensionLoader.getExtension("dubboOne"); DubboSpiTestService dubboTwo = extensionLoader.getExtension("dubboTwo"); System.out.println(dubboOne.sayHello());; System.out.println(dubboTwo.sayHello());; } }
最后运行结果为
1
2
3我是Dubbo的SPI机制的第一个实现类 我是Dubbo的SPI机制的第二个实现
3.源码的解析
我们进入到ExtensionLoader类,在上面的main方法中我们使用到的方法是getExtension,进入到这个方法。这个方法的作用是,根据传入的扩展名找到对应的实体类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public T getExtension(String name) { //检查扩展名是不是空,是空会抛出异常 if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } //如果指定的扩展名是“true”,返回的则是null,dubbo会缓存一个cachedDefaultName的string类型字段,这个字段保存的是贴有@SPI标签的类,默认是类名,也可以设置 if ("true".equals(name)) { return getDefaultExtension(); } //在已经缓存的Hodler对象的示例缓存集合中查询是否存在对应的Holder Holder<Object> holder = getOrCreateHolder(name); Object instance = holder.get(); //入伙对应的Holder不存在,则需要创建,并保存起来 if (instance == null) { synchronized (holder) { instance = holder.get(); if (instance == null) { //创建实例拓展类 //------------2------ instance = createExtension(name); //-------------2-------- //保存到缓存中 holder.set(instance); } } } return (T) instance; }
这里对上面的cachedDefaultName这个字段进行分析,找到这个字段的值进行设置的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22private String cachedDefaultName; private void cacheDefaultExtensionName() { //获取type类上的SPI标签,这里的Type是调用getExtensionLoader时候传入的,在上面的main方法中可以看到我们有传入一个类的Class对象, final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { //获取到自定义的value String value = defaultAnnotation.value(); //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]; } } } }
对createExtension方法进行解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34private T createExtension(String name) { //-------------1------------- //从配置文件中读取扩展类的路径并使用类加载器加载扩展类,默认使用的是ExtensionLoader加载器, Class<?> clazz = getExtensionClasses().get(name); if (clazz == null) { throw findException(name); } try { //获取缓存的扩展实体类 T instance = (T) EXTENSION_INSTANCES.get(clazz); //如果不存在就实例化然后保存起来 if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); instance = (T) EXTENSION_INSTANCES.get(clazz); } //----------------2------------ //将实例中注入,使用setter方式进行注入 injectExtension(instance); Set<Class<?>> wrapperClasses = cachedWrapperClasses; //如果缓存的wrapperClasses集合不是空则进行注入 if (CollectionUtils.isNotEmpty(wrapperClasses)) { //循环创建wrapperClass for (Class<?> wrapperClass : wrapperClasses) { //获取wrapperClass的构造方法,并使用创建地实例作为构造参数创建wrapperClass对象,然后像Wrapper实例中注入依赖,然后赋值给instance实例 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } }
第一步,获取所有的扩展类;第二步,创建扩展类的实例;第三步,向扩展类中进行注入依赖;第四步,把拓展对象包裹在相应的 Wrapper 对象中
解析getExtensionClasses方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96private Map<String, Class<?>> getExtensionClasses() { //获取缓存的class对象,所有的class对象保存在一个Map中,Map对象又封装在Dubbo自定义的一个Holder对象中 Map<String, Class<?>> classes = cachedClasses.get(); //如果缓存不存在则加锁,则加锁,然后再次检车是不是null,加锁的原因是避免为null的时候多个线程同时执行获取扩展类的操作 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { //获取扩展类,并缓存起来 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } //--------------------loadExtensionClasses方法 private Map<String, Class<?>> loadExtensionClasses() { //这个方法就是上面讲到的cachedDefaultName进行赋值的方法 cacheDefaultExtensionName(); Map<String, Class<?>> extensionClasses = new HashMap<>(); //按照不同的目录来加再类 loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; } //-----------loadDirectory方法------ private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { //文件名= 文件夹路径 + type 全限定名 String fileName = dir + type; try { Enumeration<java.net.URL> urls; //获取ClassLoader,默认是先获取加载ExtensionLoader类线程的上下文加载器,如果不存在才才指定ExtensionLoader类的加载器,如果加载ExtensionLoader来的加载器都不存在,则使用bootstrap类加载器 ClassLoader classLoader = findClassLoader(); if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); //加载资源,主要就是加载配置文件然后加载文件中的类,并按照键值对的形式进行存储,其中需要注意的是有对贴有Activate标签的类的特殊处理,Activate标签的作用是在符合给定情况的时候去加载这个类 loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } } //------------loadResource方法中的loadClass,loadClass方法是加载类的主要逻辑 private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException { //检查传入的type类是不是需要实例化的class的父类或者接口类,如果不是则抛错 if (!type.isAssignableFrom(clazz)) { throw new IllegalStateException("Error occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } //如果类上有Adaptive标签,则保存到对应的缓存中 if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz); } //如果是wrapper类型,则保存到对应的缓存中 else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常 clazz.getConstructor(); if (StringUtils.isEmpty(name)) { // 如果 name 为空,则尝试从 Extension 注解中获取 name,或使用小写的类名作为 name name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } }
对于实例的依赖注入方法injectExtension解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43private T injectExtension(T instance) { try { //这里的objectFactory类就是ExtensionFactory类的实现类AdaptiveExtensionFactory if (objectFactory != null) { //便利实例类的方法 for (Method method : instance.getClass().getMethods()) { //如果方法是已set开头的,且方法仅有一个参数,且方法访问级别为 public。用来确保是类的属性 if (isSetter(method)) { /** * Check {@link DisableInject} to see if we need auto injection for this property */ //如果方法上面有DisableInject这个标签就不进行注入 if (method.getAnnotation(DisableInject.class) != null) { continue; } //获取 setter 方法参数类型 Class<?> pt = method.getParameterTypes()[0]; //如果字段是基础类型或这是基础类型的数组,也不进行注入 if (ReflectUtils.isPrimitives(pt)) { continue; } try { //获取属性名,从setter方法中获取 setName 方法对应属性名 name String property = getSetterProperty(method); //获取依赖对象 Object object = objectFactory.getExtension(pt, property); if (object != null) { //调用setter方法进行以来的注入 method.invoke(instance, object); } } catch (Exception e) { logger.error("Failed to inject via method " + method.getName() + " of interface " + type.getName() + ": " + e.getMessage(), e); } } } } } catch (Exception e) { logger.error(e.getMessage(), e); } return instance; }
objectFactory 变量的类型为 AdaptiveExtensionFactory,AdaptiveExtensionFactory 内部维护了一个 ExtensionFactory 列表,用于存储其他类型的 ExtensionFactory。Dubbo 目前提供了两种 ExtensionFactory,分别是 SpiExtensionFactory 和 SpringExtensionFactory。前者用于创建自适应的拓展,后者是用于从 Spring 的 IOC 容器中获取所需的拓展。
其中SpringExtensionFactory在初始化的时候会将Spring的ApplicationContext保存在自己的内部contexts字段中,还会注册服务关闭的钩子方法和监听器,getExtension方法获取的是注册到Spring容器中的依赖对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public static void addApplicationContext(ApplicationContext context) { contexts.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); DubboShutdownHook.getDubboShutdownHook().unregister(); } BeanFactoryUtils.addApplicationListener(context, shutdownHookListener); } public <T> T getExtension(Class<T> type, String name) { //SPI should be get from SpiExtensionFactory //如果是通过自定义SPI扩展的就去SpiExtensionFactory中调用getExtension方法 if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } //获取容器中的对象 for (ApplicationContext context : contexts) { if (context.containsBean(name)) { Object bean = context.getBean(name); if (type.isInstance(bean)) { return (T) bean; } } } ..... }
在这里讲解GitHub上面最新的版本的Dubbo4.3.16版本,有部分变动。之前Dubbo版本有一个启动类DubboBootStrap类,这个类的作用是可以通过编程的方式轻松启动和停止Dubbo。其中在启动的时候会注册一个服务器关闭(这里的关闭不是强制关闭kill -9 pid 这种命令,而是kill pid这种温柔结束的方式)时候的钩子方法registerShutdownHook。这个方法会处理关闭的时候逻辑。现在这个方法在4.3.16版本中被放到了ConfigurableApplicationContext接口中,实现于AbstractApplicationContext类。在SpringExtensionFactory类中被调用。
最后
以上就是轻松长颈鹿最近收集整理的关于5.Dubbo源码分析----SPI机制的全部内容,更多相关5内容请搜索靠谱客的其他文章。
发表评论 取消回复