Dubbo源码通~ExtensionLoader
程序员文章站
2022-07-09 13:46:45
...
ExtensionLoader
1、 主要功能
- 获取
ExtensionLoader
:getExtensionLoader(Class<?> type):每个扩展应一个ExtensionLoader - 扩展类集合:
getExtensionName(Class<?> type)
- 创建扩展类对象:
getExtension(String name)
- 扩展点自动包装:Wrapper类
- 扩展点自动装配:注入依赖的属性
- 扩展点自适应:
getAdaptiveExtension()
- 扩展点自动**
- 扩展点自动**:
getActivateExtension
(URL url, String[] values, String group)
2、主要角色
-
ExtensionLoader
:扩展点加载器 -
ExtensionFactory
:- AdaptiveExtensionFactory:ExtensionFactory自适应扩展实现类
- SpiExtensionFactory:扩展点(@SPI)自适应扩展实现类工厂
-
@Adaptive
:自适应扩展 -
@Activate
:自动** -
@SPI
:扩展点
3、核心方法概述
3.1、创建ExtensionLoader
- 方法: getExtensionLoader(Class type)获得扩展接口的对应的ExtensionLoader。
- 从EXTENSION_LOADERS缓存中获取,没有则新建;
- 创建新的ExtensionLoader(先赋值两个属性:type和objectFactory):
-
若type为ExtensionFactory,则objectFactory为null
; - 其他类型,则递归调用ExtensionLoader.getExtensionLoader(ExtensionFactory.class) 创建一个ExtensionFactory对应的ExtensionLoader;
- 再通过ExtensionFactory的ExtensionLoader去加载对应的自适应扩展类AdaptiveExtensionFactory和SpiExtensionFactory。
-
- 最后为type创建的ExtensionLoader中的objectFactory对应的就是ExtensionFactory的自适应实现类。
- 每个type持有一个objectFactory的是为了后续实现属性注入
injectExtension
。
3.2、指定类获取扩展实例
-
getExtensionName(Class<?> type)指定拓展类的名称,若cachedNames缓存中没有,则获取加载配置文件中指定的所有需要拓展类,并缓存到cachedClasses。
- getExtensionClasses:获得拓展实现类数组
- loadExtensionClasses:从多个配置文件,拓展实现类数组(解析@SPI 注解,指定cachedDefaultName;加载配置文件里的实现类全限定名)
- loadDirectory:从配置文件中,加载拓展实现类数组
- loadResource:遍历文件里的所有拓展实现类,以“=”隔开的配置,key-value存储到extensionClasses中。
- loadClass:根据拓展类的注解,分开缓存各种拓展类(cachedAdaptiveClass、cachedWrapperClasses、cachedActivates、cachedNames)
-
getExtension(String name):获取指定扩展名的实现类
- createExtension(name)
- getExtensionClasses().get(name)
- 实例化扩展类:instance
- 扩展点注入:injectExtension(instance)
- 若是包装类,则instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance))
-
getAdaptiveExtension() : 扩展自适应实现类
- createAdaptiveExtension()
- 调用 getAdaptiveExtensionClass 方法获取自适应拓展 Class 对象
- 通过反射进行实例化
- 调用 injectExtension 方法向拓展实例中注入依赖
- getAdaptiveExtensionClass()
- 调用 getExtensionClasses 获取所有的拓展类
- 检查缓存,若缓存不为空,则返回缓存: cachedAdaptiveClass
- 若缓存为空,则调用 createAdaptiveExtensionClass 创建自适应拓展类。(若没有自适应扩展类,则自动创建)
- createAdaptiveExtensionClassCode(): 动态生成对应扩展接口的所有方法的实现。
- injectExtension()
- createAdaptiveExtension()
-
getActivateExtension(URL):根据URL创建自**扩展扩展类。
-
ExtensionLoader初始化示意图
-
ExtensionLoader涉及方法关联图
4、@Adaptive自适应扩展
- 触发加载自适应扩展的入口:
- getAdaptiveExtension()
- getActivateExtension()
- getDefaultExtension()
- getExtension()
每个方法内部都会主动去调用getExtensionClasses()
方法,其内部就是加载指定路径下的配置,以此来完成各种扩展类的加载。
-
调用方式:
ExtensionLoader.getExtensionLoader(Class type).getAdaptiveExtension()
- 若已经加载过对应接口的扩展实现且缓存
cachedAdaptiveInstance
中有对应的扩展实现类,则直接返回 - 若没有则再加载一遍,还是没有(一般在服务启动之后就确定加载完了),则会自动动态生成一个自适应实现类。
- 若已经加载过对应接口的扩展实现且缓存
-
生成自适应实现类需要的条件
- 需要装配的扩展实现类有被@Adaptive注解修饰。
- 扩展接口内,
必须至少有一个方法被 @Adaptive 注解修饰
,否则,所有方法没有都被@Adaptive 注解修饰的方法,则会直接在方法体内丢异常throw new UnsupportedOperationException
。
4.1、@Adaptive使用范围
Adaptive 可注解在类或方法上。
- 标注在类上:Dubbo 不会为该类生成代理类。
- 标注在方法上:Dubbo 则会为该方法生成代理逻辑,表示当前方法需要根据 参数URL 调用对应的扩展点实现。
4.2、调用方式概述
- 解决“扩展点自动装配”中如何确定装配的实现类问题?
- ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用的扩展点实现。
- 通过调用方的URL参数来确定使用哪一个扩展点实现类
- 从URL从获取扩展点的名称(接口名称)
- 通过SPI 加载具体的扩展点实现类
- 调用目标方法
4.3、举个栗子:Protocol
- Protocol接口
//☆☆---Protocol
@SPI("dubbo")
public interface Protocol {
int getDefaultPort();
//1. 服务暴露主功能入口
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
//2. 服务引用主功能入口
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
- dubbo本身没有为Protocol创建自适应扩展类,但在方法调用时会通过ExtensionLoader动态生成
package org.apache.dubbo.rpc;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.ExtensionLoader;
@Adaptive
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
//NOTE: destory()和getDefaultPort() 都没有@Adaptive注解,所以不会创建具体实现
public void destroy() {
throw new UnsupportedOperationException(
"method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException(
"method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
/**
* 服务暴露
* @param arg0
* @return
* @throws org.apache.dubbo.rpc.RpcException
*/
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) {
throw new IllegalArgumentException(
"org.apache.dubbo.rpc.Invoker argument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException(
"org.apache.dubbo.rpc.Invoker argument getUrl() == null");
}
//NOTE: 根据URL的参数来确定具体要调用哪个Protocol的扩展实现类
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = ((url.getProtocol() == null) ? "dubbo" : url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
//NOTE: 获得对应的Protocol的扩展实现类
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
//NOTE:服务暴露
return extension.export(arg0);
}
/**
* 服务引用
* @param arg0
* @param arg1
* @return
* @throws org.apache.dubbo.rpc.RpcException
*/
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0,
org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
//NOTE: 根据URL的参数来确定具体要调用哪个Protocol的扩展实现类
org.apache.dubbo.common.URL url = arg1;
String extName = ((url.getProtocol() == null) ? "dubbo" : url.getProtocol());
if (extName == null) {
throw new IllegalStateException(
"Fail to get extension(org.apache.dubbo.rpc.Protocol) name from url(" +
url.toString() + ") use keys([protocol])");
}
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
//引用服务
return extension.refer(arg0, arg1);
}
}
5、@Activate自**扩展
- 入口:getActivateExtension(URL url …)
- 对于集合类扩展,在使用时通过URL指定获取对应的扩展实现类,获取当前扩展的所有可自动**的实现。
6、实现AOP和IoC
- 通过Wrapper类来“实现”AOP。扩展点自动包装
- 什么是Wrapper类?(
有一个持有扩展点类型的构造函数的扩展点实现类
)- Wrapper类实现了扩展接口,但不是扩展点的真正实现。它从ExtensionLoader中返回的实际是Wrapper的实例,Wrapper持有实际扩展点的实现类。即Wrapper代理了扩展点,从而实现了AOP
- 什么是Wrapper类?(
- 通过
反射调用setter方法
来“实现”IoC。扩展点自动装配- 在加载扩展点时,自动注入依赖的扩展点,加载扩展点时,扩展点实现类的成员如果为其他的扩展点类类型,ExtensionLoader会自动注入依赖的扩展点。
- 实现方法: ExtensionLoader通过扫描扩展点实现类的所有setter方法来判断其成员,即ExtensionLoader会执行扩展点的拼装操作。
- 存在的问题:在加载依赖扩展点时,如何确定要加载的是哪一个实现类?
- 在加载扩展点时,自动注入依赖的扩展点,加载扩展点时,扩展点实现类的成员如果为其他的扩展点类类型,ExtensionLoader会自动注入依赖的扩展点。
//NOTE: 获得属性名,如setName方法,获得name。通过objectFactory(SpiExtensionFactory)获得依赖对象
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) {
// NOTE: 调用setter方法设置依赖
method.invoke(instance, object);
}
7、加载如Protocol等扩展点时,如何使用ExtensionLoader
- 每次在ExtensionLoader.getExtensionLoader(Class clazz)时会根据扩展点遍历其对应"META-INF/dubbo/"路径下的所有配置的扩展实现类。
- 这些扩展点的实现类统一由ExtensionFactory加载,而ExtensionFactory同样是一个扩展点,其Adaptive自适应扩展类是AdaptiveExtensionFactory。
大致加载逻辑
- 加载某一个扩展点A时,首先会为ExtensionFactory创建ExtensionLoader,同时为ExtensionFactory的扩展点加载其
自适应扩展类
AdaptiveExtensionFactory,并加载扩展实现类
SpiExtensionFactory。 - 其次为扩展点A创建对应的ExtensionLoader,并创建自适应扩展类以及加载扩展实现类。(第一步创建的ExtensionFactory创建ExtensionLoader是作为“扩展点A”的objectFactory,用于后续
属性注入
) - 为什么加载每个扩展点都需要从ExtensionFactory这个扩展点开始?
- 为了在后续创建扩展实现类对象时,对其内部进行属性赋值(即IoC属性注入),需要用到SpiExtensionFactory去创建依赖的扩展点实现类对象。
ExtensionLoader 更详细的使用,在后续更多dubbo源码解读时,会进一步分析,上述只是理解ExtensionLoader是如何工作,具有哪些功能的?
上一篇: Java SPI机制详解
下一篇: 三菱FX2N PLC 无协议通信
推荐阅读
-
Centos7通过yum跟源码编译安装Nginx
-
dubbo源码阅读之服务导出
-
Ubuntu18.04通过源码安装Odoo14的教程
-
高通公布部分骁龙865/765源码:三方ROM刷机包可以搞起了
-
Dubbo源码分析之 SPI(一)
-
dubbo源码分析01:SPI机制
-
在centos6.7通过源码安装python3.6.7报错“zipimport.ZipImportError: can't decompress data; zlib not available”
-
荐 【dubbo源码解析】--- dubbo的服务暴露+服务消费(RPC调用)底层原理深入探析
-
【Dubbo源码阅读系列】之 Dubbo XML 配置加载
-
从源码角度一步步窥探Dubbo服务发布原理