一 Dubbo SPI核心
核心实现都在ExtensionLoader中,比较重要的3个入口方法分别是
- ExtensionLoader#getExtensionLoader() 扩展实现的加载器
- ExtensionLoader#getDefaultExtension() 默认扩展实现
- ExtensionLoader#getExtension(...) 根据指定的扩展名称获取对应的扩展实现
- ExtensionLoader#getAdaptiveExtension() 获取自适应扩展
如下几个名词概念是我的个人理解
-
扩展点 - 一个接口,也就是SPI的接口,通过@SPI注解标识,
-
扩展实现 - SPI的接口具体实现,配置在classpath路径的文件中,提供给扩展加载器进行扫描
-
自适应扩展实现 - 区别于jdk SPI的关键,目的是为了在运行时根据特定场景获取需要的扩展实现,这个机制的实现体现在@Adaptive这个注解,该注解可以作用于类和方法
- @Adaptive作用于类 运行时自适应扩展类
- @Adaptive作用于方法 运行时自适应扩展方法
-
通过ExtensionLoader获取某个接口的自适应扩展实现
- 如果在配置的可扫描到的实现类上有标注@Adaptive注解就直接反射使用
- 如果没有提供@Adaptive实现类,通过编码技术生成实现
二 ExtensionLoader源码
1 getExtensionLoader()
// ExtensionLoader.java
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>(); // 缓存 扩展接口以及对应的扩展类加载器
/**
* 给定接口type的扩展类加载器ExtensionLoader
*/
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
// 必要的参数校验
if (type == null) throw new IllegalArgumentException("Extension type == null");
// 接口的扩展加载起ExtensionLoader实例都放在缓存中
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;
}
2 构造方法
// ExtensionLoader.java
private final Class<?> type; // 扩展点(接口类型)
/**
* 除了ExtensionFactory的ExtensionLoader这个属性为空 其他的扩展接口的扩展类加载器这个属性都是AdaptiveExtensionFactory的实例
* ExtensionFactory本身也是一个扩展接口
* classpath文件
* - adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
* - spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
* AdaptiveExtensionFactory实现上打上了注解@Adaptive 因此作为ExtensionFactory这个接口的默认实现
*
* objectFactory作用是为了解决可能存在的setter注入一个扩展
*/
private final ExtensionFactory objectFactory;
private ExtensionLoader(Class<?> type) {
this.type = type; // 扩展的接口
this.objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
3 扩展接口实现类扫描
// ExtensionLoader.java
/**
* 指定路径
* - META-INF/dubbo/internal/
* - META-INF/dubbo/
* - META-INF/services/
* 对上面3个路径进行扫描 找到需要扩展的接口的配置文件
* 轮询配置文件里面的所有键值对(key=名称 value=扩展点的扩展实现)
* 解析过程中
* - 如果扩展实现上注有Adaptive注解 就把这个实现缓存在cachedAdaptiveClass
* - 这个实现就是type这个扩展点的自适应扩展实现适配类 getAdaptiveExtension()方法用到
* - 扩展实现是包装类 全部缓存到cachedWrapperClasses
* - 其他扩展实现都缓存到ExtensionLoader的cachedClasses
* - getDefaultExtension()用到
* - getExtension(...)用到
*/
private Map<String, Class<?>> getExtensionClasses() {
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
classes = this.loadExtensionClasses(); // 从指定文件加载出type这个接口的实现
this.cachedClasses.set(classes);
}
}
}
return classes;
}
// ExtensionLoader.java
/**
* 3个classpath上为type接口加载具体实现
* - 内置路径+接口全限定名
* - META-INF/dubbo/internal/xxx
* - META-INF/dubbo/xxx
* - META-INF/services/xxx
* - 文件内容格式是
* - 实现1名称=实现1全限定名
* - 实现2名称=实现2全限定名
* 扫描接口type的注解SPI
* - 指定了name缓存起来作为默认实现名称
* - getDefaultExtension()使用
* 扫描到接口type的所有指定实现 按照不同场景缓存起来
* - 第一个@Adaptive注解标识的实现类缓存到cachedAdaptiveClass
* - 给getAdaptiveExtension()创建自适应扩展适配用
* - 实现类是包装类的缓存到cachedWrapperClasses
* - 其余实现类缓存到hash表中
* - getDefaultExtension()使用
* - getExtension(...)根据名称查找实现类
*
*/
private Map<String, Class<?>> loadExtensionClasses() {
/**
* 接口type存在多实现 每个实现通过名字作区别
* 将来外界通过getExtension(...)来获取type接口的实现
* - 在多实现中缓存中找需要的名称
* - 使用指派的默认实现
*/
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value(); // 指派为type接口默认实现的名称
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) this.cachedDefaultName = names[0]; // type接口默认实现的名称
}
}
// 缓存着type接口的多实现
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
/**
* 3个classpath上为type接口加载具体实现
* - 内置路径+接口全限定名
* - META-INF/dubbo/internal/xxx
* - META-INF/dubbo/xxx
* - META-INF/services/xxx
* - 文件内容格式是
* - 实现1名称=实现1全限定名
* - 实现2名称=实现2全限定名
*/
this.loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY); // META-INF/dubbo/internal/
this.loadDirectory(extensionClasses, DUBBO_DIRECTORY); // META-INF/dubbo/
this.loadDirectory(extensionClasses, SERVICES_DIRECTORY); // META-INF/services/
return extensionClasses;
}
// ExtensionLoader.java
private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir) { // 扫描结果缓存到map中
String fileName = dir + type.getName();
try {
Enumeration<java.net.URL> urls;
ClassLoader classLoader = this.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();
this.loadResource(extensionClasses, classLoader, resourceURL); // extensionClasses用于缓存第三优先级的实现类对象
}
}
} catch (Throwable t) {
logger.error("Exception when load extension class(interface: " + type + ", description file: " + fileName + ").", t);
}
}
// ExtensionLoader.java
/**
* 轮询SPI文件 遍历到的所有扩展实现中 按照3个优先级缓存
* - 优先级1 第一个标注@Adaptive注解的实现类缓存到cachedAdaptiveClass
* - 优先级2 包装类型缓存到cachedWrapperClasses
* - 其余放到hash表中
*/
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name) throws NoSuchMethodException {
if (!type.isAssignableFrom(clazz))
throw new IllegalStateException("Error when load extension class(interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + "is not subtype of interface.");
if (clazz.isAnnotationPresent(Adaptive.class)) { // 扩展实现上注有@Adaptive注解
if (this.cachedAdaptiveClass == null) // 将标注了@Adaptive注解的实现缓存起来 作为第一优先级
this.cachedAdaptiveClass = clazz;
else if (!cachedAdaptiveClass.equals(clazz)) // 扩展实现中只能存在一个标注了@Adaptive注解的实现
throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName() + ", " + clazz.getClass().getName());
} else if (this.isWrapperClass(clazz)) { // 扩展实现是包装类
Set<Class<?>> wrappers = this.cachedWrapperClasses;
if (wrappers == null) {
this.cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = this.cachedWrapperClasses;
}
wrappers.add(clazz);
} else { // 其他的扩展实现
clazz.getConstructor();
if (name == null || name.length() == 0) {
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 (names != null && names.length > 0) {
Activate activate = clazz.getAnnotation(Activate.class);
if (activate != null)
this.cachedActivates.put(names[0], activate);
for (String n : names) {
if (!this.cachedNames.containsKey(clazz)) cachedNames.put(clazz, n);
Class<?> c = extensionClasses.get(n);
if (c == null) {
extensionClasses.put(n, clazz);
} else if (c != clazz)
throw new IllegalStateException("Duplicate extension " + type.getName() + " name " + n + " on " + c.getName() + " and " + clazz.getName());
}
}
}
}
4 获取扩展接口的实现
基于上面已经将接口的实现类扫描出来并分别缓存起来
下述几种获取接口扩展实现逻辑大致差不多
- 找到具体的实现类对象
- 通过反射创建类实例对象
- 防止实例对象通过setter方法注入属性对象也是需要扩展的,使用objectFactory指向的AdaptiveExtensionFactory对象处理
4.1 扩展实现类对象
4.1.1 自适应扩展实现getAdaptiveExtension()
// ExtensionLoader.java
/**
* 当前扩展接口的自适应扩展实现
* - 取缓存
* - 创建扩展点的自适应扩展实现
* - 实现类反射创建实例对象
* - 扫文件路径过程中缓存着@Adaptive标识的实现类
* - 没有指定@Adaptive标识 编码实现
* - 对实例对象setter方法检查
* - 存缓存
*/
@SuppressWarnings("unchecked")
public T getAdaptiveExtension() {
Object instance = this.cachedAdaptiveInstance.get();
// 典型的synchronized DCL
if (instance == null) {
if (createAdaptiveInstanceError == null) {
synchronized (cachedAdaptiveInstance) {
instance = cachedAdaptiveInstance.get(); // 双检查
if (instance == null) {
try {
instance = this.createAdaptiveExtension(); // 为扩展接口创建自适应扩展实现
cachedAdaptiveInstance.set(instance); // 放缓存
} catch (Throwable t) {
createAdaptiveInstanceError = t;
throw new IllegalStateException("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
// ExtensionLoader.java
/**
* 依赖ExtensionLoader这个扩展实现加载器 为扩展点type这个接口创建合适的实现
* - 先找到自适应扩展实现类对象
* - 扫描Dubbo SPI路径过程着缓存下找到的@Adaptive标识的实现类对象
* - 编码技术生成类
* - 根据找到的类对象反射创建实例
* - 用AdaptiveExtensionFactory检查实例的setter方法 注入循环扩展对象
*/
@SuppressWarnings("unchecked")
private T createAdaptiveExtension() {
try {
/**
* - getAdaptiveExtensionClass()加载出自适应实现
* - 优先级1 Dubbo SPI指定扫描路径上有@Adaptive标识的实现
* - 优先级2 编码技术生成
* - newInstance() 对选择出来的唯一的type接口实现 通过反射创建实现的实例
* - injectExtension()
* - 解决扩展实现的setter属性注入依赖的问题
*/
return this.injectExtension((T) this.getAdaptiveExtensionClass().newInstance());
} catch (Exception e) {
throw new IllegalStateException("Can not create adaptive extension " + type + ", cause: " + e.getMessage(), e);
}
}
// ExtensionLoader.java
/**
* 自适应扩展实现类的优先级
* - 缓存着的cachedAdaptiveClass 也就是扫描Dubbo SPI路径上实现类第一个@Adaptive注解标识的类对象
* - 编码技术实现
*/
private Class<?> getAdaptiveExtensionClass() {
this.getExtensionClasses(); // 加载所有扩展接口指定的实现方式
if (this.cachedAdaptiveClass != null) // 扫描加载落站实现的时候会将@Adaptive注解标识的实现缓存起来作为扩展适配的第一优先级
return this.cachedAdaptiveClass;
return this.cachedAdaptiveClass = this.createAdaptiveExtensionClass(); // 没有@Adaptive注解指定默认的扩展实现 使用编码技术生成
}
4.1.2.1 扫描出来@Adaptive注解标识的实现类对象
4.1.2.2 编码技术生成类对象
// ExtensionLoader.java
/**
* code生成的方式对扩展点中被{@link Adaptive}修饰的方法进行编码 字节码技术生成类
*/
private Class<?> createAdaptiveExtensionClass() {
String code = this.createAdaptiveExtensionClassCode(); // 硬编码扩展接口的实现类(方法标注@Adaptive()注解的)
ClassLoader classLoader = findClassLoader(); // 当前类加载器
com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
return compiler.compile(code, classLoader);
}
4.1.2 默认实现getDefaultExtension()
// ExtensionLoader.java
public T getDefaultExtension() { // 在hash表缓存的实现类对象找@SPI指定的名称
/**
* 扫描Dubbo SPI指定的3个classpath路径
* - 将接口type指定的实现缓存起来
* - 实现上有注解Adaptive的单独缓存到cachedAdaptiveClass
* - 实现类是包装类对象 缓存到cachedWrapperClasses
* - 其他实现类对象缓存到hash表
* - 将接口type注解SPI指定的默认实现名称缓存起来
*/
this.getExtensionClasses();
if (null == cachedDefaultName || cachedDefaultName.length() == 0 || "true".equals(cachedDefaultName))
return null;
return this.getExtension(cachedDefaultName);
}
4.1.3 指定名称的实现getExtension(...)
// ExtensionLoader.java
/**
* 接口type的实现类缓存优先级
* - 实现类上第一个打上@Adaptive注解的缓存在cachedAdaptiveClass
* - 实现类是包装类的缓存在cachedWrapperClasses
* - 其他实现类缓存在hash表中
* - getExtension(...)就是从这份缓存中找指定名称的实现类
* - getDefaultExtension(...)是根据接口上@SPI的名称作为依据在这个缓存中找的实现类
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {
if (name == null || name.length() == 0) throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) return this.getDefaultExtension(); // type接口注解@SPI标识的名称 对应的实现作为接口默认实现
Holder<Object> holder = this.cachedInstances.get(name);
if (holder == null) {
cachedInstances.putIfAbsent(name, new Holder<Object>());
holder = cachedInstances.get(name);
}
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get(); // 接口type扩展的实现
if (instance == null) {
instance = this.createExtension(name); // 缓存里面没有就去扫描里面找
holder.set(instance);
}
}
}
return (T) instance;
}
// ExtensionLoader.java
/**
* 指定路径
* - META-INF/dubbo/internal/
* - META-INF/dubbo/
* - META-INF/services/
* 对上面3个路径进行扫描 找到需要扩展的接口的配置文件
* 轮询配置文件里面的所有键值对(key=名称 value=扩展点的扩展实现)
* 解析过程中
* - 优先级1 扩展实现上首个注有Adaptive注解 缓存在cachedAdaptiveClass
* - 这个实现类就是type这个扩展点的自适应扩展实现适配类 getAdaptiveExtension()方法用到
* - 优先级2 扩展实现是包装类 全部缓存到cachedWrapperClasses
* - 优先级3 其他扩展实现都缓存到ExtensionLoader的cachedClasses
* - getDefaultExtension()用到
* - getExtension(...)用到
*
* 到优先级3的cachedClasses找指定名字的实现类
*/
@SuppressWarnings("unchecked")
private T createExtension(String name) {
Class<?> clazz = this.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);
}
this.injectExtension(instance); // 通过AdaptiveExtensionFactory检查实现的实例有没有setter注入扩展的场景
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
for (Class<?> wrapperClass : wrapperClasses)
instance = this.injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
return instance;
} catch (Throwable t) {
throw new IllegalStateException("Extension instance(name: " + name + ", class: " + type + ") could not be instantiated: " + t.getMessage(), t);
}
}
4.2 反射创建类实例对象
4.3 setter注入扩展
// ExtensionLoader.java
/**
* instance是type接口众多实现中最终选取的唯一作为Dubbo中的实现
* 解决扩展实现的循环依赖场景问题
* - 某个扩展点的扩展实现已经获取 其中存在一个setter方法 设置的属性本身又是一个扩展实现
*/
private T injectExtension(T instance) {
try {
/**
* ExtensionFactory的ExtensionLoader的objectFactory为空 其他都不是空
* - ExtensionLoader<ExtensionFactory> objectFactory是空 直接返回的实例就是AdaptiveExtensionFactory的实现实例
* - ExtensionLoader<T> objectFactory指向了ExtensionLoader<ExtensionFactory>对象
*/
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
// setter方法
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
/**
* 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]; // setxxx这个setter方法的形参 肯定只有一个参数
try {
// setxxx这个setter方法注入的属性名称xxx
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
/**
* Duboo是否存在SPI实现
* - name是property
* - 接口类型是pt
* 获取setter参数的扩展实现 目的是为了解决扩展实现里面的setter属性注入的依赖
*/
Object object = this.objectFactory.getExtension(pt, property);
if (object != null)
method.invoke(instance, object);
} catch (Exception e) {
logger.error("fail to inject via method " + method.getName()
+ " of interface " + type.getName() + ": " + e.getMessage(), e);
}
}
}
}
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return instance;
}
三 Dubbo SPI例子
1 ProxyFactory扩展适配
/**
* <p>{@link ProxyFactory}扩展点被{@link com.alibaba.dubbo.common.extension.SPI}标注 {@link SPI#value()}属性为javassist 该扩展点的方法都被{@link com.alibaba.dubbo.common.extension.Adaptive}修饰</p>
* <p>扩展实现候选为<ul>
* <li>stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper</li>
* <li>jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory</li>
* <li>javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory</li>
* </ul></p>
* <p>自适应扩展实现的选取步骤为<ul>
* <li>首先候选列表中没有标注{@link com.alibaba.dubbo.common.extension.Adaptive}的类</li>
* <li>生成code</li>
* </ul></p>
* @since 2022/5/19
* @author dingrui
*/
public class ProxyFactoryTest {
public static void main(String[] args) {
ExtensionLoader<ProxyFactory> extensionLoader = ExtensionLoader.getExtensionLoader(ProxyFactory.class);
ProxyFactory proxyFactory = extensionLoader.getAdaptiveExtension();
System.out.println(proxyFactory);
ProxyFactory extension = (ProxyFactory) extensionLoader.getExtension("javassist");
System.out.println(extension);
}
}
1 看扩展点,扩展接口被@SPI注解标识,value属性是javassist,总共3个方法,都被@Adaptive标识,而且注解属性Contants.PROXY_KEY就是proxy;由此可以推测,这个接口的自适应适配类是通过生成code的方式,然后在实现方法过程中尝试从URL配置项中获取扩展点名称,没有指定名称,就使用这个@SPI注解传进去的javassist作为默认扩展点名称
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
/**
* ProxyFactory. (API/SPI, Singleton, ThreadSafe)
*/
/**
* <p>配置信息都封装在了{@link URL}中 通过{@link URL#getParameter}获取指定key的配置 如果不存在指定key的配置 就使用默认值defaultValue</p>
* <p>通过生成code的方式对当前扩展点的{@link Adaptive}修饰的方法进行生成 调用{@link com.alibaba.dubbo.common.extension.ExtensionLoader#getExtension}方式生成自适应扩展实现
* 扩展名称从{@link URL}中获取{@link Constants#PROXY_KEY} 如果不存在这个key的配置值 就使用给定默认值javassist</p>
*/
@SPI("javassist")
public interface ProxyFactory {
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker) throws RpcException;
/**
* create proxy.
*
* @param invoker
* @return proxy
*/
@Adaptive({Constants.PROXY_KEY})
<T> T getProxy(Invoker<T> invoker, boolean generic) throws RpcException;
/**
* create invoker.
*
* @param <T>
* @param proxy
* @param type
* @param url
* @return invoker
*/
@Adaptive({Constants.PROXY_KEY})
<T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;
}
2 看配置文件的实现,根据包package com.alibaba.dubbo.rpc找到配置文件
stub=com.alibaba.dubbo.rpc.proxy.wrapper.StubProxyFactoryWrapper
jdk=com.alibaba.dubbo.rpc.proxy.jdk.JdkProxyFactory
javassist=com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory
这3个实现没有被@Adaptive标识的
3 通过生成code方式创建自适应扩展实现对象
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0, boolean arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0, arg1);
}
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
if (arg2 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
}
4 看代码实现,很清晰可以看到尝试从URL配置信息根据key=protocol来获取,这个key就是从扩展接口的方法@Adaptive注解属性传进来的,作为方法的扩展点实现,如果在URL中没有找到配置项,就使用默认值,这个默认值又是从扩展接口@SPi注解传进来的属性值javassist
2 Compiler扩展适配
/**
* <p>{@link Compiler}扩展点类上标注了{@link sun.security.provider.ConfigFile.Spi} 并且注解的{@link SPI#value()}的值是javassist 方法级别没有{@link com.alibaba.dubbo.common.extension.Adaptive}</p>
* <p>classpath配置了3个实现 并且有注有{@link com.alibaba.dubbo.common.extension.Adaptive}的实现 因此这个实现被选为扩展实现<ul>
* <li>adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler</li>
* <li>jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler</li>
* <li>javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler</li>
* </ul></p>
* <p>扩展点的实现为{@link com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler}</p>
* @since 2022/5/19
* @author dingrui
*/
public class CompilerTest {
public static void main(String[] args) {
ExtensionLoader<Compiler> extensionLoader = ExtensionLoader.getExtensionLoader(Compiler.class);
Compiler adaptiveExtension = extensionLoader.getAdaptiveExtension();
System.out.println(adaptiveExtension);
}
}
package com.alibaba.dubbo.common.compiler;
@SPI("javassist")
public interface Compiler {
/**
* Compile java source code.
*
* @param code Java source code
* @param classLoader classloader
* @return Compiled class
*/
Class<?> compile(String code, ClassLoader classLoader);
}
1 看扩展点,Compiler这个扩展点只有一个方法,没有使用@Adaptive,也就是说没办法通过生成code的方式生成自适应扩展对象,那么一定是从配置的实现候选里面选出来的
2 看实现的配置文件,Compile这个接口的package是com.alibaba.dubbo.common.compiler,找到dubbo工程的dubbo-common模块的classpath:META-INF/dubbo/internal/com.alibaba.dubbo.common.compiler这个文件,里面内容如下``
adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler
jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler
javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler
里面的第一个实现类就是被@Adaptive注解标识了,因此这个类的实例对象就是Compiler的自适应扩展实现
3 看下这个类实现的接口方法逻辑
/**
* <p>{@link Compiler}扩展点类上标注了{@link sun.security.provider.ConfigFile.Spi} 并且注解的{@link SPI#value()}的值是javassist 方法级别没有{@link com.alibaba.dubbo.common.extension.Adaptive}</p>
* <p>classpath配置了3个实现 并且有注有{@link com.alibaba.dubbo.common.extension.Adaptive}的实现 因此这个实现被选为扩展实现<ul>
* <li>adaptive=com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler</li>
* <li>jdk=com.alibaba.dubbo.common.compiler.support.JdkCompiler</li>
* <li>javassist=com.alibaba.dubbo.common.compiler.support.JavassistCompiler</li>
* </ul></p>
* <p>扩展点的自适应实现为{@link com.alibaba.dubbo.common.compiler.support.AdaptiveCompiler}</p>
*
* <p>但是调用这个方法的时候并不是直接使用某个解析器进行解析 而是实现了类似工厂模式的工厂类 根据扩展名称<tt>name</tt>获取真正的扩展实现{@link Compiler}</p> 这个name开放了api赋值可以从外部设置进来 也可以使用默认的(扩展点上{@link Compiler}的注解属性{@link SPI#value()}就是默认扩展名)
*/
@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);
}
在实现类的compile()方法内部会根据扩展名获取到真正的实现
3 ExtensionFactory扩展适配
/**
* <p>{@link ExtensionFactory}这个扩展点只在类上标识了{@link com.alibaba.dubbo.common.extension.SPI}注解 方法级别没有{@link com.alibaba.dubbo.common.extension.Adaptive}</p>
* <p>因此在classpath配置文件里的扩展实现上一定有一个实现类是标注了{@link com.alibaba.dubbo.common.extension.Adaptive}的<ul>
* <li>adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory</li>
* <li>spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory</li>
* </ul></p>
* <p>所以自适应扩展点的实现是{@link com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory}</p>
* @since 2022/5/19
* @author dingrui
*/
public class ExtensionFactoryTest {
public static void main(String[] args) {
ExtensionLoader<ExtensionFactory> extensionLoader = ExtensionLoader.getExtensionLoader(ExtensionFactory.class);
ExtensionFactory obj = extensionLoader.getAdaptiveExtension();
System.out.println(obj);
}
}
1 看扩展点,扩展点只有一个方法,并且没有标注@Adaptive注解,说明这个扩展点的自适应实现一定是从配置文件里面选出来的
package com.alibaba.dubbo.common.extension;
/**
* ExtensionFactory
*/
@SPI
public interface ExtensionFactory {
/**
* Get extension.
*
* @param type object type.
* @param name object name.
* @return object instance.
*/
<T> T getExtension(Class<T> type, String name);
}
2 看配置文件的实现,根据扩展点package找到配置文件com.alibaba.dubbo.common.extension
adaptive=com.alibaba.dubbo.common.extension.factory.AdaptiveExtensionFactory
spi=com.alibaba.dubbo.common.extension.factory.SpiExtensionFactory
3 其中第一个实现类是被@Adaptive注解修饰的,因此这个类就是扩展点的自适应扩展适配类
@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;
}
}
4 Protocol扩展适配
/**
* <p>{@link Protocol}扩展点标注有{@link com.alibaba.dubbo.common.extension.SPI}注解 并且{@link SPI#value()}属性为dubbo 而且扩展点有两个方法被{@link com.alibaba.dubbo.common.extension.Adaptive}标注</p>
* <p>classpath配置的扩展实现候选有<ul>
* <li>filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper</li>
* <li>listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper</li>
* <li>mock=com.alibaba.dubbo.rpc.support.MockProtocol</li>
* </ul>
* 这3个实现不存在被{@link com.alibaba.dubbo.common.extension.Adaptive}标注的类 有2个是包装类</p>
* 因此这个扩展点的自适应扩展实现是通过字节码编码反射方式创建出来的对象
* @since 2022/5/19
* @author dingrui
*/
public class ProtocolTest {
public static void main(String[] args) {
ExtensionLoader<Protocol> extensionLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
Protocol refprotocol = extensionLoader.getAdaptiveExtension();
System.out.println(refprotocol);
}
}
1 看扩展点,接口被@SPI注解修饰,并且value属性为dubbo,其中有2个方法被@Adaptive修饰,以上信息反映两个信息
- 它的自适应扩展实现可能是在配置文件中指定的@Adaptive实现
- 在实现逻辑中不再需要SPI扩展
- 大概率在实现中通过SPI根据扩展名获取真正的扩展点实现,扩展名的默认名称是通过这个@SPI注解传进来的dubbo
- 它的自适应扩展实现是通过生成code的方式,实现了这个接口中被@Adaptive修饰的2个方法,然后在方法中通过URL配置传进来的配置项进行获取真正的扩展点,URL配置key是硬编码的protocol,不存在配置值是使用@SPI传进来的dubbo
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;
/**
* Protocol. (API/SPI, Singleton, ThreadSafe)
*/
@SPI("dubbo")
public interface Protocol {
/**
* Get default port when user doesn't config the port.
*
* @return default port
*/
int getDefaultPort();
/**
* Export service for remote invocation: <br>
* 1. Protocol should record request source address after receive a request:
* RpcContext.getContext().setRemoteAddress();<br>
* 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
* export the same URL<br>
* 3. Invoker instance is passed in by the framework, protocol needs not to care <br>
*
* @param <T> Service type
* @param invoker Service invoker
* @return exporter reference for exported service, useful for unexport the service later
* @throws RpcException thrown when error occurs during export the service, for example: port is occupied
*/
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
/**
* Refer a remote service: <br>
* 1. When user calls `invoke()` method of `Invoker` object which's returned from `refer()` call, the protocol
* needs to correspondingly execute `invoke()` method of `Invoker` object <br>
* 2. It's protocol's responsibility to implement `Invoker` which's returned from `refer()`. Generally speaking,
* protocol sends remote request in the `Invoker` implementation. <br>
* 3. When there's check=false set in URL, the implementation must not throw exception but try to recover when
* connection fails.
*
* @param <T> Service type
* @param type Service class
* @param url URL address for the remote service
* @return invoker service's local proxy
* @throws RpcException when there's any error while connecting to the service provider
*/
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
/**
* Destroy protocol: <br>
* 1. Cancel all services this protocol exports and refers <br>
* 2. Release all occupied resources, for example: connection, port, etc. <br>
* 3. Protocol can continue to export and refer new service even after it's destroyed.
*/
void destroy();
}
2 看配置文件的实现,到package com.alibaba.dubbo.rpc这个包里面找
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
3 这3个实现都没有被@Adaptive注解修饰
4 通过生成code方式
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
public void destroy() {throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}