【Dubbo】SPI 机制原理分析 --自适应扩展点(Adaptive)
前篇: 【Dubbo】SPI 机制原理分析 —静态扩展点
首先来看一个概念,扩展的自适应实例。如果称它为扩展代理类,可能更好理解些,扩展的自适应实例其实就是一个 Extension 的代理,它实现了扩展点接口。在调用扩展点的接口方法时,会根据实际的参数来决定要使用哪个扩展。
比如一个 IRepository 的扩展点,有一个 save 方法。有两个实现 MysqlRepository 和 MongoRepository。IRepository的自适应实例在调用接口方法的时候,会根据 save 方法中的参数,来决定要调用哪个 IRepository的实现。
- 如果方法参数中有 repository=mysql,那么就调用 MysqlRepository 的 save 方法。
- 如果 repository=mongo,就调用 MongoRepository 的 save 方法。
和面向对象的延迟绑定很类似。
问题一:为什么Dubbo会引入扩展自适应实例的概念呢?
首先,Dubbo中的配置有两种。一种是固定的系统级别的配置,在 Dubbo 启动之后就不会再改了。还有一种是运行时的配置,可能对于每一次的 RPC,这些配置都不同。
第一种很好理解,这里解释一下第二种。因为 dubbo 是 url驱动,即服务的配置信息都是通过&
拼接在 url 之后,换句话说,当 Provider 收到调用请求时,其相关配置是通过查 url 后的参数获得;这样做的目的是, Consumer 在注册中心拿到相应服务的 url 后,可以根据自身的配置对请求 url 再次进行拼接(修改)。因此,对于 Dubbo 而言,每一次的 RPC 调用的参数都是未知的,只有在运行时,根据这些参数才能做出正确的决定。
那么问题就来了,在 Spring bean 实例化时,如果它依赖某个扩展点,在进行依赖注入时,如何知道该使用哪个具体的扩展实现?所以,这时候就需要一个代理模式了,它实现了扩展点接口,方法内部可以根据运行时参数,动态的选择合适的扩展实现。而这个代理就是自适应实例。
自适应扩展实例在 Dubbo 中的使用非常广泛,Dubbo中,每一个扩展都会有一个自适应类,如果我们没有提供,Dubbo会使用字节码工具为我们自动生成一个。所以我们基本感觉不到自适应类的存在。
问题二:如何标识自适应扩展呢?
@Adaptive 就是一个自适应扩展点的标识。它可以修饰在类上,也可以修饰在方法上面。
类级别:
- 用于具体扩展
- 代表实现一个装饰类,类似于设计模式中的装饰模式,它主要作用是返回指定类
- 目前在整个系统中 AdaptiveCompiler、AdaptiveExtensionFactory 这两个类拥有该注解
方法级别:
- 用于扩展点(接口)中的方法
- 表示需要生成一个动态代理,方法内部会根据方法的参数,来决定使用哪个扩展
PS:说简单点,如果作用在类上,就相当于自己定义了个现成的代理;如果作用在方法上,运行时会动态生成一个 Adaptive 实例。
1.代码示例
1.1 类级别
示例,传入扩展点(Compiler 接口),返回 AdaptiveCompiler。
// 注意,使用自适应模式时,调用的方法是 getAdaptiveExtension(),不是 getExtension(key)。
Compiler compiler = ExtensionLoader.getExtensionLoader(Compiler.class).getAdaptiveExtension();
System.out.println(compiler.getClass());
问题一:getAdaptiveExtension() 为什么是返回 AdaptiveCompiler 呢?
我们来看看 META-INF/dubbo/internal/org.apache.dubbo.common.compiler.Compiler
AdaptiveCompiler 被加载就是因为第一行的原因?错,是因为 getAdaptiveExtension() 表示获取适配器。
问题二:AdaptiveCompiler 有什么用?
打开 AdaptiveCompiler 类,看到这个类上面有一个注解@Adaptive。
=> AdaptiveCompiler其实就是个适配器,适配的是 JdkCompiler 和 JavassistCompiler(真正提供扩展点实现)
问题三:那这两种实现,到底该使用哪种?或者说如何适配的?
@Adaptive
public class AdaptiveCompiler implements Compiler {
private static volatile String DEFAULT_COMPILER;
public static void setDefaultCompiler(String compiler) {
DEFAULT_COMPILER = compiler;
}
// 关注compile方法,看他调用jdkCompiler还是javassistCompiler的compile方法
@Override
public Class<?> compile(String code, ClassLoader classLoader) {
Compiler compiler;
ExtensionLoader<Compiler> loader = ExtensionLoader.getExtensionLoader(Compiler.class);
// name
String name = DEFAULT_COMPILER;
// 首先根据name进行选择
if (name != null && name.length() > 0) {
compiler = loader.getExtension(name);
// 否则加载默认扩展点实现
} else {
compiler = loader.getDefaultExtension();
}
return compiler.compile(code, classLoader);
}
}
1.2 方法级别
比如 Protocol 接口,它里面定义了 export 和 refer 两个抽象方法,这两个方法分别带有@Adaptive 的标识,标识是一个自适应方法。
Protocol 是一个通信协议接口,具体有多种实现,谁来执行这个选择呢?答:运行时生成的动态代理 Protocol$Adaptive
可以看到,如果 url 中没有携带 Protocol 设置,就采用默认的 dubbo 协议,然后再通过 ExtensionLoader 去获取具体的扩展实例(到这到这里其实就是静态扩展点了)。
这里的方法层面的 Adaptive 就决定了当前这个方法会采用何种协议来发布服务。
2.源码分析
其中原理从 getAdaptiveExtension() 开始分析
getAdaptiveExtension()
public T getAdaptiveExtension() {
// cachedAdaptiveInstance是一个缓存,在dubbo中大量用到了这种内存缓存
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("Failed to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("Failed to create adaptive instance: " +
createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
createAdaptiveExtension()
private T createAdaptiveExtension() {
try {
// 1.获得一个自适应扩展点实例
// 2.进行依赖注入
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can't create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
getAdaptiveExtensionClass()
private Class<?> getAdaptiveExtensionClass() {
getExtensionClasses();
if (cachedAdaptiveClass != null) {
return cachedAdaptiveClass;
}
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
cachedAdaptiveClass 是一个类级别自适应扩展点,表示告诉 dubbo spi loader,“我是一个自适应扩展点,来加载我吧”。
cachedAdaptiveClass 应该是在加载解析 /META-INF/dubbo 下的扩展点的时候加载进来的。在加载完之后如果这个类有@Adaptive 标识,则会赋值赋值而给 cachedAdaptiveClass
如果 cachedAdaptiveClass 不存在,dubbo 会动态生成一个代理类 Protocol$Adaptive.(前面的名字 protocol 是根据当前 ExtensionLoader 所加载的扩展点来定义的)
createAdaptiveExtensionClass()
动态生成字节码,然后进行动态加载。如果加载的是 Protocol.class,应该是 Protocol$Adaptive
private Class<?> createAdaptiveExtensionClass() {
// 生成动态代理的类的字符串
// 这个 cachedDefaultName 实际上就是扩展点接口的@SPI 注解对应的名字
// 如果此时加载的是 Protocol.class,那么 cachedDefaultName=dubbo
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);
}
injectExtension()
对于扩展点进行依赖注入,简单来说就是如果当前加载的扩展点中存在一个成员属性(对象),并且提供了 set 方法,那么这个方法就会执行依赖注入
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
// objectFactory这里用到了
// 获得实例对应的方法,判断方法是否是一个set方法
for (Method method : instance.getClass().getMethods()) {
if (isSetter(method)) {
// 可以选择禁用依赖注入
if (method.getAnnotation(DisableInject.class) != null) {
continue;
}
// 获得方法的参数,这个参数必须是一个对象类型并且是一个扩展点
Class<?> pt = method.getParameterTypes()[0]; // 获得这个方法的参数类型
if (ReflectUtils.isPrimitives(pt)) {
// 如果不是对象类型,就跳过
continue;
}
try {
// 获得这个方法的属性名称
String property = getSetterProperty(method);
// 根据class以及name,使用自适应扩展点进行加载,并赋值到当前set方法中
Object object = objectFactory.getExtension(pt, property);
if (object != null) {
// 调用set方法进行赋值
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;
}
在 injectExtension 这个方法中,入口处的代码首先判断了 objectFactory 这个对象是否为空。这个是在哪里初始化的呢? 实际上在获得 ExtensionLoader 的时候,就对 objectFactory 进行了初始化。
然后通过 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()
去获得一个自适应的扩展点。进入 ExtensionFactory 这个接口,可以看到它是一个扩展点
并且有一个自己实现的自适应扩展点 AdaptiveExtensionFactory
/**
*@Adaptive 加载到类上表示这是一个自定义的适配器类,表示我们再调用 getAdaptiveExtension 方法的时候,
*不需要走上面这么复杂的过程。会直接加载到 AdaptiveExtensionFactory。
*然后在 getAdaptiveExtensionClass()方法处有判断
*/
@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
// 我们可以看到除了自定义的自适应适配器类以外,还有两个实现类,一个是 SPI,一个是 Spring,
// 轮询这 2 个,从一个中获取到就返回。
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;
}
}
还没有评论,来说两句吧...