我是靠谱客的博主 野性跳跳糖,这篇文章主要介绍dubbo 扩展点加载机制,现在分享给大家,希望可以做个参考。

概述

扩展点加载机制在dubbo框架中无处不在, 它也是dubbo 高扩展性的关键实现. 所以要彻底理解dubbo的实现原理, 弄明白扩展点加载机制至关重要.

dubbo 的扩展点机制本质上是在Java SPI 基础 上改造的. 那么什么是Java 的SPI 呢

所有技术的衍生都是基于业务需要,因为有需要,才会想解决办法, 进而产生各种技术. 那么Java的SPI 的产生又是基于什么样的需求呢?
我们知道JDBC是为了统一数据库的操作而由Sun 公司定制的一套接口标准, 而具体的实现则由各个数据库厂商提供, 比如我们使用mysql数据库,那么我们就要引用mysql的依赖, 这个依赖就是对JDBC的实现. 那么Java 虚拟机怎么知道接口的实现是哪个呢, 就是通过Java的SPI 机制

SPI机制的核心类是ServiceLoader, 通过调用ServiceLoader.load(接口.class) 方法加载该接口的所有实现.
load 方法的实现原理是 去 METSA_INF/services 目录寻找文件名为该接口名的文件, 然后逐行加载该文件中写的接口的实现类

使用JDBC的例子来理解就是 Java 定制了一套数据库接口, 然后数据库厂商提供实现类,然后在他们项目的 META-INF/services包 创建文件名为接口全类名的文件, 文件内容写他对这个接口的实现类的全类名.

比如我们引入了mysql的依赖, Java虚拟机通过这个规则就会加载到对应的实现了.

我们可以看到使用这种加载机制, 可以很好的将接口和实现分离, 极大的提高了灵活性和扩展性.

dubbo 扩展点加载机制在此基础上做了扩展和改进

  1. dubbo 加载的路径包括 META-INF/services, META-INF/dubbo 和 META-INF/dubbo/internal
  2. dubbo 可以按扩展名来使用不用的扩展类, Java SPI 是将实现类的全类名直接写在文件中, 而 dubbo 的形式是 扩展名=扩展实现全类名,因此可以按扩展名去使用对应的扩展类
  3. dubbo 是按需实例化的, 使用Java的ServiceLoader.load 方法加载完实现类,它会对所有的实现类都实例化. 而dubbo 是根据你要使用的那个扩展名来实例化对应的实现的, 其他的扩展类放在缓存中
  4. dubbo 增加实现了 类似AOP和IOC的功能

三个关键注解

dubbo 扩展点加载机制有三个重要的注解,分别是

  1. @SPI: 主要是标记功能, 标记一个接口是Dubbo SPI 接口, 也就是表明它是一个扩展点, 可以有多个不同的实现
  2. @Adaptive: 标注在类上, 表示这个类是该扩展点的默认实现, 最多只能有一个扩展点实现使用这个注解, 也就是一个扩展点的自适应实现智能有一个; 标注在方法上, dubbo 框架会为这个扩展点生成一个默认的自适应实现.
  3. @Activate: 主要的使用场景是 在不同的场景中需要激活不同的扩展点实现

ExtensionLoader的实现原理

ExtensionLoader 的构造方法是私有的, 使用方法是通过调用静态方法来获取一个ExtensionLoader实例,然后调用getExtension(String name) 方法来加载以name为扩展名的实现
即常见用法是

复制代码
1
2
ExtensionLoader.getExtensionLoader(扩展点.class).getExtension(扩展名)

getExtensionLoader 方法首先会检查传入的类型是否为一个合法的扩展点,也就是是否是一个有@SPI 注解的接口
然后从 EXTENSION_LOADERS 缓存中获取该type对应的扩展点加载器,如果缓存中没有再新建一个, 为什么要从缓存中获取扩展点加载器呢?
因为dubbo 对扩展点的实现设置了很多缓存, 比如扩展点的实现类,自适应类,包装类等等, 这些缓存就保存在这个扩展点对应的扩展点加载器中.

可以看到在构造方法中 objectFactory 也是通过加载扩展点实现来赋值的, objectFactory 具体在给扩展点注入其他扩展点属性的时候会使用到, ExtensionFactory 在后面再详细解析

复制代码
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
// 静态的,全局唯一 private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64); public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { if (type == null) { throw new IllegalArgumentException("Extension type == null"); } if (!type.isInterface()) { throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!"); } if (!withExtensionAnnotation(type)) { throw new IllegalArgumentException("Extension type (" + type + ") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!"); } ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); if (loader == null) { EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type)); loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type); } return loader; } // 私有的静态方法 private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }

ExtensionLoader 有三个获取扩展点实现的方法, 分别是 getExtension, getAdaptiveExtension 和getActivateExtension 分别获取普通扩展点实现, 自适应扩展点实现, 自动激活的扩展点实现

普通扩展类: 除了自适应扩展类, 包装扩展类,其他的就是普通扩展类
自适应扩展类: 有@Adaptive注解的扩展类, 或者扩展点接口中的方法有@Adaptive注解的, 此时 dubbo 框架会动态生成一个
自动激活扩展点: 主要使用在有多个扩展实现, 需要根据不同条件被激活的场景, 比如Filter需要多个同时被激活.

getExtension

参数说明:
name: 扩展名
wrap: 是否对扩展实例进行包装, 默认为真, 封装后面会详细解析
总体流程:
1. 如果name 为"true" 则使用默认的实现,后面会解析
2. 先从cachedInstances 普通扩展实例缓存中获取, 如果没有先new一个Holder占个坑
3. 然后使用double-check 机制对holder加锁, 然后调用 createExtension 创建实例,在放到holder中
这里有个小问题,为什么要使用holder,并且加锁呢? cachedInstances 已经使用了 ConcurrentHashMap,可以保证线程安全啊?
其中一个原因, 是因为要获取一个扩展点的实例, 需要加载这个扩展点的所有实现类, 实例化之后还要对实例进行依赖注入和包装, 最后才是一个完整的实例,如果直接在实例化完成后放入ConcurrentHashMap, 由 ConcurrentHashMap 保证只有一个 实例, 那么可能会导致多个线程都执行了实例化的过程, 而最后只有一个线程的结果被采用, 造成浪费

复制代码
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
public T getExtension(String name) { return getExtension(name, true); } public T getExtension(String name, boolean wrap) { if (StringUtils.isEmpty(name)) { throw new IllegalArgumentException("Extension name == null"); } if ("true".equals(name)) { return getDefaultExtension(); } 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; } private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>(); private Holder<Object> getOrCreateHolder(String name) { Holder<Object> holder = cachedInstances.get(name); if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } return holder; } // Holder 实现 public class Holder<T> { // volatile 保证了线程间可见 private volatile T value; public void set(T value) { this.value = value; } public T get() { return value; } }

createExtension

总体流程:
1. 通过 getExtensionClasses 方法获取该type类型的扩展点的所有实现类, 然后拿到扩展名为name的实现类
2. 从EXTENSION_INSTANCES 缓冲中获取该扩展类的实例,如果没有则新构造一个
3. 调用 injectExtension(instance) 方法对该实例进行属性注入
4. 如果要进行包装, 则遍历排序后的 cachedWrapperClasses (在getExtensionClasses过程中会进行缓存), 用 instance 去实例化包装类,然后对包装类的实例调用 injectExtension 方法进行属性注入之后, 作为新的 instance. 我们看到如果有多个包装类会进行层层包装
5. 调用initExtension(instance) 方法

复制代码
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
// 静态的,全局唯一, 一个扩展点实现类只会保存一个实例 private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64); private T createExtension(String name, boolean wrap) { Class<?> clazz = getExtensionClasses().get(name); if (clazz == null || unacceptableExceptions.contains(name)) { throw findException(name); } try { T instance = (T) EXTENSION_INSTANCES.get(clazz); if (instance == null) { EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.getDeclaredConstructor().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 void initExtension(T instance) { if (instance instanceof Lifecycle) { Lifecycle lifecycle = (Lifecycle) instance; lifecycle.initialize(); } }

getExtensionClasses

一样的套路,先尝试从缓存中获取, 如果没有再通过 loadExtensionClasses 加载
这里同样是锁着占位坑 holder

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); 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

总体逻辑:
1. 使用cacheDefaultExtensionName 方法将扩展点 @SPI注解中的value 作为 默认扩展名缓存在 cachedDefaultName 中, 在上述的name 为 “true” 时返回默认扩展实现中会使用到
2. 遍历所有的加载策略, 通过 loadDirectory 方法加载各个加载策略的路径下的该扩展点的所有实现, 这里调用两次 loadDirectory 方法是为了兼容dubbo 贡献给apache之前的实现

加载策略 strategies 也是通过java SPI 来实现的, dubbo 默认提供的LoadingStrategy 一共有三种, 分别对应三个加载路径
org.apache.dubbo.common.extension.DubboInternalLoadingStrategy ----> META-INF/dubbo/internal
org.apache.dubbo.common.extension.DubboLoadingStrategy ------> META-INF/dubbo
org.apache.dubbo.common.extension.ServicesLoadingStrategy -------> META-INF/services

复制代码
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
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; } private static final Pattern NAME_SEPARATOR = Pattern.compile("\s*[,]+\s*"); private void cacheDefaultExtensionName() { final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation == null) { return; } 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]; } } } private static volatile LoadingStrategy[] strategies = loadLoadingStrategies(); private static LoadingStrategy[] loadLoadingStrategies() { return stream(ServiceLoader.load(LoadingStrategy.class).spliterator(), false) .sorted() .toArray(LoadingStrategy[]::new); }
loadDirectory

总体逻辑:
1. 调用 类加载器的 getResources 方法获取所有的对应路径下名为扩展点全类名的文件
2. 调用 loadResource 方法解析每个文件

复制代码
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
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type, boolean extensionLoaderClassLoaderFirst, boolean overridden, String... excludedPackages) { String fileName = dir + type; try { Enumeration<java.net.URL> urls = null; ClassLoader classLoader = findClassLoader(); // try to load from ExtensionLoader's ClassLoader first if (extensionLoaderClassLoaderFirst) { ClassLoader extensionLoaderClassLoader = ExtensionLoader.class.getClassLoader(); if (ClassLoader.getSystemClassLoader() != extensionLoaderClassLoader) { urls = extensionLoaderClassLoader.getResources(fileName); } } if (urls == null || !urls.hasMoreElements()) { if (classLoader != null) { urls = classLoader.getResources(fileName); } else { urls = ClassLoader.getSystemResources(fileName); } } if (urls != null) { while (urls.hasMoreElements()) { java.net.URL resourceURL = urls.nextElement(); loadResource(extensionClasses, classLoader, resourceURL, overridden, excludedPackages); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } }
loadResource

总体逻辑:

  1. 逐行解析文件, 截取 注释 # 前面的部分,使用 = 分隔,分别获取扩展名和实现类名
  2. 调用 Class.forName(clazz, true, classLoader) 加载实现类
  3. 调用 loadClass 进行分类缓存
复制代码
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
private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL, boolean overridden, String... excludedPackages) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; String clazz = null; 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(); // 扩展类全路径 clazz = line.substring(i + 1).trim(); } else { // 兼容 Java SPI clazz = line; } if (StringUtils.isNotEmpty(clazz) && !isExcluded(clazz, excludedPackages)) { loadClass(extensionClasses, resourceURL, Class.forName(clazz, true, classLoader), name, overridden); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException( "Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } }

总体逻辑:

  1. 如果该扩展类有@Adaptive注解, 将它缓存在 cachedAdaptiveClass
  2. 如果该扩展类是包装类(即实现了type接口,又将type接口作为构造器参数), 将它缓存在cachedWrapperClasses 中, 在上述提到的包装过程中会使用
  3. 其他的就是普通的扩展类,缓存在cachedClasses 中, 对于普通扩展类,还会调用一个方法 cacheActivateClass 会将扩展名和Activate 注解缓存在 cachedActivates ,在 getActivateExtension 中会使用
复制代码
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
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name, boolean overridden) throws NoSuchMethodException { 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."); } if (clazz.isAnnotationPresent(Adaptive.class)) { cacheAdaptiveClass(clazz, overridden); } else if (isWrapperClass(clazz)) { cacheWrapperClass(clazz); } else { clazz.getConstructor(); if (StringUtils.isEmpty(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, n, overridden); } } } } private void cacheActivateClass(Class<?> clazz, String name) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { cachedActivates.put(name, activate); } else { // support com.alibaba.dubbo.common.extension.Activate com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class); if (oldActivate != null) { cachedActivates.put(name, oldActivate); } } }

injectExtension

总体逻辑:

  1. 如果该扩展点加载器没有objectFactory,则不进行属性注入, 通过构造函数,我们知道只有 ExtensionFactory 会不进行属性注入
  2. 遍历该实例的所有setter方法, 而且是没有禁止注入注解DisableInject 的方法, 对于基本类型也不进行注入,
  3. 通过截取setter方法获得属性名, 然后去ExtensionFactory 扩展点工厂找实例, 然后注入
复制代码
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
private T injectExtension(T instance) { if (objectFactory == null) { return instance; } try { for (Method method : instance.getClass().getMethods()) { if (!isSetter(method)) { continue; } /** * Check {@link DisableInject} to see if we need auto injection for this property */ if (method.getAnnotation(DisableInject.class) != null) { continue; } Class<?> pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { String property = getSetterProperty(method); Object object = objectFactory.getExtension(pt, property); if (object != null) { 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; } private String getSetterProperty(Method method) { return method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : ""; }

getAdaptiveExtension

一样的套路,先尝试从缓冲中获取自适应实例,如果没有,则占坑,加锁, 调用 createAdaptiveExtension 创建自适应实例

复制代码
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
private final Holder<Object> cachedAdaptiveInstance = new Holder<>(); public T getAdaptiveExtension() { Object instance = cachedAdaptiveInstance.get(); if (instance == null) { if (createAdaptiveInstanceError != null) { throw new IllegalStateException("Failed to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError); } synchronized (cachedAdaptiveInstance) { instance = cachedAdaptiveInstance.get(); if (instance == null) { try { instance = createAdaptiveExtension(); cachedAdaptiveInstance.set(instance); } catch (Throwable t) { createAdaptiveInstanceError = t; throw new IllegalStateException("Failed to create adaptive instance: " + t.toString(), t); } } } } return (T) instance; }

createAdaptiveExtension, getAdaptiveExtensionClass

  1. createAdaptiveExtension 方法
    调用getAdaptiveExtensionClass 获取自适应扩展类, 进行实例化后,在进行属性注入
    这里我们看到对于自适应扩展实例不会进行包装
  2. getAdaptiveExtensionClass 方法
  • 先调用getExtensionClasses方法,通过上面我们知道该方法会加载type扩展点的所有类型,并进行分类缓存, 如果有@Adaptive 注解的实现类,则会缓存在 cachedAdaptiveClass
  • 调用createAdaptiveExtensionClass 直接生成自适应类的字符串形式的代码,然后使用动态编译器编译 得到自适应扩展类
  • AdaptiveClassCodeGenerator 生成代码的规则如下
    1. 像我们编写一般的Java类一样,首先写包名,import, 类名,该类名为扩展点类名加$Adaptive后缀, 实现的类,继承的类等
    2. 类的内容是逐一生成扩展点的方法实现, 对于没有@Adaptive注解的方法, 生成空实现, 对于有@Adaptive注解的方法,在生成的方法中会调用

ExtensionLoader.getExtensionLoader(扩展点).getExtension(extName)

那么extName怎么获取呢, 在URL中找 @Adaptive 中声明的key , 如果有拿value作为扩展名,如果 @Adaptive 没有声明key, 那么用. 分隔驼峰类名得到extName, 如果没有对应的扩展类实现, 则使用默认扩展名
获取扩展点实现后,会调用该扩展点的该接口方法.
比如对于Transporter,会生成Transporter$Adaptive 类, 生成的 bind 方法会 获取扩展点实现后,调用bind 方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private T createAdaptiveExtension() { try { return injectExtension((T) getAdaptiveExtensionClass().newInstance()); } catch (Exception e) { throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e); } } private Class<?> getAdaptiveExtensionClass() { getExtensionClasses(); if (cachedAdaptiveClass != null) { return cachedAdaptiveClass; } return cachedAdaptiveClass = createAdaptiveExtensionClass(); } private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }
复制代码
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
@SPI("netty") public interface Transporter { /** * Bind a server. * * @param url server url * @param handler * @return server * @throws RemotingException * @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...) */ @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY}) RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException; /** * Connect to a server. * * @param url server url * @param handler * @return client * @throws RemotingException * @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...) */ @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY}) Client connect(URL url, ChannelHandler handler) throws RemotingException; }

getActivateExtension

  1. 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
  2. 如果names中不包含-default
  • 调用 getExtensionClasses, 通过上面我们知道这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在 cachedActivates , 在此时则需要使用该缓存
  • 遍历 cachedActivates , 如果names中不包含name, 也不包含-name, 并且@Activate中group和values能和group 和URL中的参数匹配,则激活该扩展名

这个方法的逻辑比较绕, 关键是我们弄清楚要激活哪些扩展类即可

  1. 先从URL中获取key 对应的值,如果有多个则用, 分隔,作为names
  2. 激活分为两部分,一部分是从缓存 cachedActivates 中获取. 通过上面我们知道getExtensionClasses这个函数去加载type 扩展点的所有实现, 在加载完之后进行分类缓存时,对于普通扩展类,如果有@Activate 注解, 会将扩展名和Activate 注解缓存在cachedActivates; 另一部分是激活 names中满足条件的
  3. 对于第一部分, 如果names 中没有-default, 那么会去缓存 cachedActivates 中查找能适配group和URL的扩展名,如果匹配就激活.
  4. 对于第二部分, 遍历每个names, 如果names 中不包含-name, 那么激活
    [说明] default代表所有@Activate集合, -xxxx 则表示不激活xxxx
复制代码
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
public List<T> getActivateExtension(URL url, String key, String group) { String value = url.getParameter(key); return getActivateExtension(url, StringUtils.isEmpty(value) ? null : COMMA_SPLIT_PATTERN.split(value), group); } public List<T> getActivateExtension(URL url, String[] values, String group) { List<T> activateExtensions = new ArrayList<>(); // solve the bug of using @SPI's wrapper method to report a null pointer exception. TreeMap<Class, T> activateExtensionsMap = new TreeMap<>(ActivateComparator.COMPARATOR); Set<String> loadedNames = new HashSet<>(); Set<String> names = CollectionUtils.ofSet(values); if (!names.contains(REMOVE_VALUE_PREFIX + DEFAULT_KEY)) { getExtensionClasses(); for (Map.Entry<String, Object> entry : cachedActivates.entrySet()) { String name = entry.getKey(); Object activate = entry.getValue(); String[] activateGroup, activateValue; if (activate instanceof Activate) { activateGroup = ((Activate) activate).group(); activateValue = ((Activate) activate).value(); } else if (activate instanceof com.alibaba.dubbo.common.extension.Activate) { activateGroup = ((com.alibaba.dubbo.common.extension.Activate) activate).group(); activateValue = ((com.alibaba.dubbo.common.extension.Activate) activate).value(); } else { continue; } if (isMatchGroup(group, activateGroup) && !names.contains(name) && !names.contains(REMOVE_VALUE_PREFIX + name) && isActive(activateValue, url) && !loadedNames.contains(name)) { activateExtensionsMap.put(getExtensionClass(name), getExtension(name)); loadedNames.add(name); } } if (!activateExtensionsMap.isEmpty()) { activateExtensions.addAll(activateExtensionsMap.values()); } } List<T> loadedExtensions = new ArrayList<>(); // 遍历URL中指定key的值names, 对于每个值name, 如果name 不是以- 开头 而且 names中也不包含-name, 那么激活该name for (String name : names) { if (!name.startsWith(REMOVE_VALUE_PREFIX) && !names.contains(REMOVE_VALUE_PREFIX + name)) { if (!loadedNames.contains(name)) { if (DEFAULT_KEY.equals(name)) { if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(0, loadedExtensions); loadedExtensions.clear(); } } else { loadedExtensions.add(getExtension(name)); } loadedNames.add(name); } else { // If getExtension(name) exists, getExtensionClass(name) must exist, so there is no null pointer processing here. String simpleName = getExtensionClass(name).getSimpleName(); logger.warn("Catch duplicated filter, ExtensionLoader will ignore one of them. Please check. Filter Name: " + name + ". Ignored Class Name: " + simpleName); } } } if (!loadedExtensions.isEmpty()) { activateExtensions.addAll(loadedExtensions); } return activateExtensions; } // 遍历@Activate 注解的values数组, 对于每个值用: 分隔,得到key, keyValue, 然后遍历URL中的参数(k,v形式), 如果参数k与key 相同或者后缀是.key 而且 keyValue 与v 相同或者 v 不为空, 那么返回true, 激活该扩展名 // 大概含义 就是 URL中包含@Activate 注解中values 声明的值,那么应该激活 private boolean isActive(String[] keys, URL url) { if (keys.length == 0) { return true; } for (String key : keys) { // @Active(value="key1:value1, key2:value2") String keyValue = null; if (key.contains(":")) { String[] arr = key.split(":"); key = arr[0]; keyValue = arr[1]; } for (Map.Entry<String, String> entry : url.getParameters().entrySet()) { String k = entry.getKey(); String v = entry.getValue(); if ((k.equals(key) || k.endsWith("." + key)) && ((keyValue != null && keyValue.equals(v)) || (keyValue == null && ConfigUtils.isNotEmpty(v)))) { return true; } } } return false; }

ExtensionFactory的实现原理

在对扩展实例进行属性注入,使用到了objectFactory, 通过构造函数,我们知道它是ExtensionFactory的自适应实现

复制代码
1
2
3
4
5
6
private ExtensionLoader(Class<?> type) { this.type = type; objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); }

ExtensionFactory 有三个实现类

  • AdaptiveExtensionFactory
  • SpiExtensionFactory
  • SpringExtensionFactory

其中, AdaptiveExtensionFactory 有@Adaptive注解, 通过上述 对getAdaptiveExtension 方法的流程分析, 如果有扩展类上有@Adaptive注解,则直接作为自适应实现, 所以 objectFactory 为 AdaptiveExtensionFactory

AdaptiveExtensionFactory

AdaptiveExtensionFactory 会在构造函数中初始化factories, 然后在执行 getExtension 方法时,调用每个factory的getExtension 方法. 那么factories是什么呢
通过查看ExtensionLoader 的 getSupportedExtensions 方法, factories 就是getExtensionClasses 方法的返回结果,而getExtensionClasses 是普通扩展类, 所以factories也就是 SpiExtensionFactory 和 SpringExtensionFactory
综上, AdaptiveExtensionFactory 是自适应实现,它是统管 SpiExtensionFactory 和 SpringExtensionFactory 的

复制代码
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
@Adaptive public class AdaptiveExtensionFactory implements ExtensionFactory { private final List<ExtensionFactory> factories; public AdaptiveExtensionFactory() { ExtensionLoader<ExtensionFactory> loader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class); List<ExtensionFactory> list = new ArrayList<ExtensionFactory>(); for (String name : loader.getSupportedExtensions()) { list.add(loader.getExtension(name)); } factories = Collections.unmodifiableList(list); } @Override public <T> T getExtension(Class<T> type, String name) { for (ExtensionFactory factory : factories) { T extension = factory.getExtension(type, name); if (extension != null) { return extension; } } return null; } } // ExtensionLoader 的 getSupportedExtensions 方法 public Set<String> getSupportedExtensions() { Map<String, Class<?>> clazzes = getExtensionClasses(); return Collections.unmodifiableSet(new TreeSet<>(clazzes.keySet())); }

SpiExtensionFactory

getExtension 方法返回type类型 的自适应扩展实例

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SpiExtensionFactory implements ExtensionFactory { @Override public <T> T getExtension(Class<T> type, String name) { if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { ExtensionLoader<T> loader = ExtensionLoader.getExtensionLoader(type); if (!loader.getSupportedExtensions().isEmpty()) { return loader.getAdaptiveExtension(); } } return null; } }

SpringExtensionFactory

从 getExtension 方法可以看到 它是遍历SpringContext, 然后从Spring上下文中获取满足条件的实例的.那么Spring上下文是什么时候注入的, 是在 ReferenceBean, ServiceBean 和 ConfigCenterBean 初始化时注入的.
也就是dubbo 的属性注入不但可以注入其他的扩展点实例,还可以从spring容器中查找bean注入

复制代码
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
public class SpringExtensionFactory implements ExtensionFactory { private static final Logger logger = LoggerFactory.getLogger(SpringExtensionFactory.class); private static final Set<ApplicationContext> CONTEXTS = new ConcurrentHashSet<ApplicationContext>(); public static void addApplicationContext(ApplicationContext context) { CONTEXTS.add(context); if (context instanceof ConfigurableApplicationContext) { ((ConfigurableApplicationContext) context).registerShutdownHook(); // see https://github.com/apache/dubbo/issues/7093 DubboShutdownHook.getDubboShutdownHook().unregister(); } } public static void removeApplicationContext(ApplicationContext context) { CONTEXTS.remove(context); } public static Set<ApplicationContext> getContexts() { return CONTEXTS; } // currently for test purpose public static void clearContexts() { CONTEXTS.clear(); } @Override @SuppressWarnings("unchecked") public <T> T getExtension(Class<T> type, String name) { //SPI should be get from SpiExtensionFactory if (type.isInterface() && type.isAnnotationPresent(SPI.class)) { return null; } for (ApplicationContext context : CONTEXTS) { T bean = getOptionalBean(context, name, type); if (bean != null) { return bean; } } //logger.warn("No spring extension (bean) named:" + name + ", try to find an extension (bean) of type " + type.getName()); return null; } } public static <T> T getOptionalBean(ListableBeanFactory beanFactory, String beanName, Class<T> beanType) throws BeansException { if (beanName == null) { return getOptionalBeanByType(beanFactory, beanType); } T bean = null; try { bean = beanFactory.getBean(beanName, beanType); } catch (NoSuchBeanDefinitionException e) { // ignore NoSuchBeanDefinitionException } catch (BeanNotOfRequiredTypeException e) { // ignore BeanNotOfRequiredTypeException logger.warn(String.format("bean type not match, name: %s, expected type: %s, actual type: %s", beanName, beanType.getName(), e.getActualType().getName())); } return bean; } private static <T> T getOptionalBeanByType(ListableBeanFactory beanFactory, Class<T> beanType) { // Issue : https://github.com/alibaba/spring-context-support/issues/20 String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, beanType, true, false); if (beanNames == null || beanNames.length == 0) { return null; } else if (beanNames.length > 1) { throw new NoUniqueBeanDefinitionException(beanType, Arrays.asList(beanNames)); } return (T) beanFactory.getBean(beanNames[0]); }

扩展点动态编译的实现

dubbo 动态生成的自适应扩展类只是字符串, 还需要编译才能得到真正的Class.

从如下代码可以看到,dubbo先是生成类的字符串code, 然后获取Compiler的自适应实现 来编译code 得到Class

复制代码
1
2
3
4
5
6
7
8
private Class<?> createAdaptiveExtensionClass() { String code = new AdaptiveClassCodeGenerator(type, cachedDefaultName).generate(); ClassLoader classLoader = findClassLoader(); org.apache.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(org.apache.dubbo.common.compiler.Compiler.class).getAdaptiveExtension(); return compiler.compile(code, classLoader); }

Compiler 有两个子类 AdaptiveCompiler, AbstractCompiler, AbstractCompiler 的也有两个子类 JavassistCompiler 和 JdkCompiler

和 ExtensionFactory 类似, AdaptiveCompiler 也是自适应实现,是管理 JavassistCompiler 和 JdkCompiler 的
AbstractCompiler 只是实现了一些通用逻辑,具体的编译工作由子类 JavassistCompiler 和 JdkCompiler 实现

AdaptiveCompiler

AdaptiveCompiler 的compile 方法, 先获取默认的编译器DEFAULT_COMPILER, 如果没有才调用ExtensionLoader的getDefaultExtension获得
getDefaultExtension 是加载cachedDefaultName 缓存的扩展名, 也就是扩展点@SPI注解中声明的值, Compiler声明的是javassist, 所以也就是如果没有设置 DEFAULT_COMPILER,默认使用的编译器是 JavassistCompiler
而通过代码搜索得知, DEFAULT_COMPILER 是在ApplicationConfig实例化的注入的,通过专栏的下一章得知 该属性是在解析配置文件中的<dubbo:application compiler=‘jdk’/> 赋值 的, 所以如果想使用 JdkCompiler 编译器,可以在配置文件配置

具体的编译工作由 JavassistCompiler 或者 JdkCompiler完成, 暂时就不再详细解析了,分别用到了 Javassist 和jdk原生的编译器.
后续再在Java字节码专栏进行详细讲解

复制代码
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
@Adaptive public class AdaptiveCompiler implements Compiler { private static volatile String DEFAULT_COMPILER; public static void setDefaultCompiler(String compiler) { DEFAULT_COMPILER = compiler; } @Override public Class<?> compile(String code, ClassLoader classLoader) { Compiler compiler; ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class); String name = DEFAULT_COMPILER; // copy reference if (name != null && name.length() > 0) { compiler = loader.getExtension(name); } else { compiler = loader.getDefaultExtension(); } return compiler.compile(code, classLoader); } } //ExtensionLoader 的getDefaultExtension public T getDefaultExtension() { getExtensionClasses(); if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } return getExtension(cachedDefaultName); }

最后

以上就是野性跳跳糖最近收集整理的关于dubbo 扩展点加载机制的全部内容,更多相关dubbo内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(115)

评论列表共有 0 条评论

立即
投稿
返回
顶部