半日闲

因过竹院逢僧话,又得浮生半日闲

0%

Dubbo源码解读7-3.X版本基于SPI机制的扩展点发现技术

框架版本

dubbo 3.0.5
3.0版本中,在原有的ExtensionLoader之上,设计了ExtensionDirector类,用于构建类似ClassLoader一样的父子级管理器。
至此,所有Dubbo接口扩展点将使用ExtensionDirector类的getExtensionLoader(Class type)方法来获取,用于替代ExtensionLoader.getExtensionLoader(Class type)(该接口目前还存在,但标记为过时调用,未来可能遗弃)。

一、ExtensionDirector 扩展点管理

ExtensionDirector的父子关系与ScopeModel相关,一般一个ScopeModel绑定一个ExtensionDirector。ScopeModel有三个子类,分别为FrameworkModel,ApplicationModel,ModuleModel,父子关系为FrameworkModel->ApplicationModel->ModuleModel。创建扩展点加载器的时候仿照类加载器的双亲委托机制。

代码解读

定义三个扩展点管理范围,分别为Framework(1), Application(2), Module(3)以及无实现的Self(4)。然后框架根据不同的应用场景创建出不同范围的扩展点。权值越低的范围创建的时候,会委托其父级帮助其创建,一直到Framework级别。
相关代码:

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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//ScopeModel.java
public ScopeModel(ScopeModel parent, ExtensionScope scope) {
this.parent = parent;
this.scope = scope;
}

protected void initialize() {
this.extensionDirector = new ExtensionDirector(parent != null ? parent.getExtensionDirector() : null, scope, this);
this.extensionDirector.addExtensionPostProcessor(new ScopeModelAwareExtensionProcessor(this));
this.beanFactory = new ScopeBeanFactory(parent != null ? parent.getBeanFactory() : null, extensionDirector);
this.destroyListeners = new LinkedList<>();
this.attributes = new ConcurrentHashMap<>();
this.classLoaders = new ConcurrentHashSet<>();

// Add Framework's ClassLoader by default
ClassLoader dubboClassLoader = ScopeModel.class.getClassLoader();
if (dubboClassLoader != null) {
this.addClassLoader(dubboClassLoader);
}
}

//FrameworkModel.java
public FrameworkModel() {
super(null, ExtensionScope.FRAMEWORK);
this.setInternalId(String.valueOf(index.getAndIncrement()));
// register FrameworkModel instance early
synchronized (globalLock) {
allInstances.add(this);
resetDefaultFrameworkModel();
}
if (LOGGER.isInfoEnabled()) {
LOGGER.info(getDesc() + " is created");
}
initialize();
}

//ApplicationModel.java
public ApplicationModel(FrameworkModel frameworkModel, boolean isInternal) {
super(frameworkModel, ExtensionScope.APPLICATION);
Assert.notNull(frameworkModel, "FrameworkModel can not be null");
this.isInternal = isInternal;
this.frameworkModel = frameworkModel;
frameworkModel.addApplication(this);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(getDesc() + " is created");
}
initialize();
}

//ModuleModel.java
public ModuleModel(ApplicationModel applicationModel, boolean isInternal) {
super(applicationModel, ExtensionScope.MODULE);
Assert.notNull(applicationModel, "ApplicationModel can not be null");
this.applicationModel = applicationModel;
applicationModel.addModule(this, isInternal);
if (LOGGER.isInfoEnabled()) {
LOGGER.info(getDesc() + " is created");
}

initialize();
Assert.notNull(serviceRepository, "ModuleServiceRepository can not be null");
Assert.notNull(moduleConfigManager, "ModuleConfigManager can not be null");
Assert.assertTrue(moduleConfigManager.isInitialized(), "ModuleConfigManager can not be initialized");

// notify application check state
ApplicationDeployer applicationDeployer = applicationModel.getDeployer();
if (applicationDeployer != null) {
applicationDeployer.notifyModuleChanged(this, DeployState.PENDING);
}
}

//ExtensionDirector.java
public <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
checkDestroyed();
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() + "!");
}

// 1. find in local cache
ExtensionLoader<T> loader = (ExtensionLoader<T>) extensionLoadersMap.get(type);

ExtensionScope scope = extensionScopeMap.get(type);
if (scope == null) {
SPI annotation = type.getAnnotation(SPI.class);
scope = annotation.scope();
extensionScopeMap.put(type, scope);
}

if (loader == null && scope == ExtensionScope.SELF) {
// create an instance in self scope
loader = createExtensionLoader0(type);
}

// 2. find in parent
if (loader == null) {
if (this.parent != null) {
loader = this.parent.getExtensionLoader(type);
}
}

// 3. create it
if (loader == null) {
loader = createExtensionLoader(type);
}

return loader;
}

二、LoadingStrategy(扩展点加载策略)

代码解读

扩展点加载策略是依托于原生的SPI技术, ServiceLoader类。
LoadingStrategy有四个实现,分别对应四种扩展点配置的路径
DubboExternalLoadingStrategy: MET-INF/dubbo/external/xxxx
DubboInternalLoadingStrategy: MET-INF/dubbo/internal/xxxx
DubboLoadingStrategy: MET-INF/dubbo/xxxx
ServicesLoadingStrategy: MET-INF/services/xxxx
例子:

listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
name=实现类全路径

三、扩展点相关注解

  • org.apache.dubbo.common.extension.SPI
  • org.apache.dubbo.common.extension.Adaptive
  • org.apache.dubbo.common.extension.Activate
    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
    public @interface SPI {

    /**
    * 扩展实现名称,SPI配置文件中的每一项的name
    */
    String value() default "";

    /**
    * 扩展点范围,与扩展点管理类相关
    */
    ExtensionScope scope() default ExtensionScope.APPLICATION;
    }
    public @interface Adaptive {
    /**
    * 适配类里面进行扩展点调用的配置值
    */
    String[] value() default {};
    }
    public @interface Activate {
    /**
    * 分组
    */
    String[] group() default {};

    /**
    * 配置值
    */
    String[] value() default {};

    @Deprecated
    String[] before() default {};

    @Deprecated
    String[] after() default {};

    /**
    * 顺序
    */
    int order() default 0;
    }

四、扩展点加载解析 ExtensionLoader类

类加载除了上述的差异之外,除了拥抱原生而编写的dubbo-native模块之外,和2.7的解析逻辑没有太大的出入。

4.1 getExtension(name,wrap)

描述: 获取扩展实现, 
参数:
name: 扩展实现名称  
wrap: 是否包装, 如果是false,则只返回名称对应的扩展实现类实例对象, 如果是true, 则返回扩展点的装饰器类,并根据activate注解的order来进行排序,权值越低,则装饰得越晚,最终返回权值最低的装饰器类的实例。

4.2 getAdaptiveExtension(name)

Compiler运用动态代理技术,生成适配层的动态对象。
描述: 获取扩展实现的适配类,在4.1的基础上,通过动态字节码技术生成$Adapter类,如果扩展点接口的方法没有用@Adaptive修饰,则抛出异常。3.0+版本为了迎合Graavl虚拟机的部署,dubbo框架提供了dubbo-native模块,应对在动态字节码技术无法使用,具体生成$Adapter实际类。