1 Dubbo SPI
区别于JDK原生的SPI,【Dubbo】自己实现了一套SPI机制实时加载具体的实现类。org.apache.dubbo.common.extension.ExtensionLoader类是Dubbo SPI机制的核心,看一下【ExtensionLoader】的几个核心方法。
public class ExtensionLoader<T> {
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<>(64);
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<>(64);
private final Class<?> type;
private final ExtensionFactory objectFactory;
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<>();
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>();
private final Map<String, Object> cachedActivates = new ConcurrentHashMap<>();
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<>();
private final Holder<Object> cachedAdaptiveInstance = new Holder<>();
private volatile Class<?> cachedAdaptiveClass = null;
private String cachedDefaultName;
private Set<Class<?>> cachedWrapperClasses;
private ExtensionLoader(Class<?> type) {
this.type = type;
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
}
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {}
/**
* Get activate extensions.
*
* @param url url
* @param values extension point names
* @param group group
* @return extension list which are activated
* @see org.apache.dubbo.common.extension.Activate
*/
public List<T> getActivateExtension(URL url, String[] values, String group) {}
/**
* Find the extension with the given name. If the specified name is not found, then {@link IllegalStateException}
* will be thrown.
*/
@SuppressWarnings("unchecked")
public T getExtension(String name) {}
@SuppressWarnings("unchecked")
public T getAdaptiveExtension(){}
}
【ExtensionLoader】使用了单例模式,既有静态成员、又有实例成员。
1.1 【ExtensionLoader】静态成员
- EXTENSION_LOADERS 静态变量 ConcurrentHashMap 用来存储各SPI接口对应【ExtensionLoader】实例,每个SPI接口的ExtensionLoader都是一个单例,key是SPI接口类;
- EXTENSION_INSTANCES 静态变量 ConcurrentHashMap 用来存储所有已加载的实现类的实例, key是实现类;
- <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) 静态方法 获取SPI接口(type)的ExtensionLoader实例;
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Extension type == null");
}
//笔者注 不是接口类型 不允许创建ExtensionLoader
if (!type.isInterface()) {
throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
}
//笔者注 没有@SPI注解 也不允许创建ExtensionLoader
if (!withExtensionAnnotation(type)) {
throw new IllegalArgumentException("Extension type (" + type +
") is not an extension, because it is NOT annotated with @" + SPI.class.getSimpleName() + "!");
}
//笔者注 如果有创建过则直接使用,没有则创建一个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;
}
1.2 【ExtensionLoader】实例成员
- type 实例变量 当前【ExtensionLoader】实例关联的SPI接口;
- objectFactory 实例变量 ExtensionFactory 用来给SPI接口实例自动注入属性,类似于Spring框架的IOC;
- cachedNames 实例变量 ConcurrentMap 缓存实现类对应的扩展名
- cachedClasses 实例变量 用来缓存扩展名对应的实现类
- cachedActivates 实例变量 ConcurrentHashMap 如果实现类有@Activate注解,则将注解缓存下来
- cachedInstances 实例变量 ConcurrentHashMap 用来缓存扩展名对应的实现类实例
- cachedAdaptiveInstance 实例变量 用来缓存SPI接口的Adaptive类的实例
- cachedAdaptiveClass 实例变量 用来缓存SPI接口的Adaptive类
- cachedDefaultName 实例变量 用来缓存SPI接口的默认实现类的扩展名,注解@SPI的value
- cachedWrapperClasses 实例变量 缓存SPI接口的包装类
- T getExtension(String name) 实例方法 获取扩展名[name]对应的实现类实例
从磁盘中(META-INF/dubbo/external、META-INF/dubbo/internal、META-INF/dubbo、META-INF/services)加载对应SPI接口的实现类,找到指定扩展名[name]的类,创建实例并自动注入相关属性。
自动注入只对有且仅有一个参数且参数类型不是基本类型的setter方法,AdaptiveExtensionFactory.getExtension,通过实现类SpringExtensionFactory跟Spring容器结合起来注入Spring的Bean。
public class ExtensionLoader<T> {
//笔者注:如果已经加载过则使用,否则加载实现类并返回name对应的实例
public T getExtension(String name) {
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) {
//笔者注 当前是loader中没有缓存,则去创建
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
//笔者注 创建name对应扩展实例
private T createExtension(String name) {
//笔者注 获取name对应实现类
Class<?> clazz = 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);
}
//笔者注 自动注入属性
injectExtension(instance);
//笔者注 如果有包装类 则包装对应的实例
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
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);
}
}
// 笔者注: 对实例自动注入属性,只对只有一个参数且参数类型不是基本类型(Integer、Double等)的setter方法注入。
//注入的对象是通过ExtensionFactory的Adaptive类AdaptiveExtensionFactory获取,
//AdaptiveExtensionFactory跟Spring的容器结合起来了
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);
//笔者注:objectFactory是ExtensionFactory的自适应类(AdaptiveExtensionFactory)的实例,
//getExtension从两个地方取值(1)pt对应spi的自适应(Adaptive)类实例 (2)Spring容器中pt对应的实例
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;
}
//笔者注 取SPI接口的实现类,首先从缓存取,取不到则从磁盘文件里加载实现类
//(META-INF/dubbo/external、META-INF/dubbo/internal、META-INF/dubbo、META-INF/services)
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;
}
}
- T getAdaptiveExtension() 实例方法 获取SPI接口对应的Adaptive类的实例。
Dubbo有两种方法创建Adaptive类,其一是在某个实现类上添加注解@Adaptive则表明这个方法就是该SPI接口的Adaptive类,其二是Dubbo自动生成一个Adaptive类文件并编译成class对象。
自动生成的Adaptive类只对SPI接口中有@Adaptive注解的方法做处理,没有@Adaptive注解的方法生成的方法体直接是"throw new UnsupportedOperationException"。有注解的方法生成的方法体实现的逻辑是:
- 找URL对象:类型URL的参数,没有则在其它参数中寻找"返回类型为URL"的方法并获得URL对象,没有则报错。
- 在URL中依次找keys集合中的key对应的value作为实际扩展名,都没找到则使用SPI接口默认扩展名;keys集合来源于方法上@Adaptive注解的value数组;如果数组为空,则用SPI接口的类名(驼峰结构转为xx.yy.zz形式);实际的代码形如String extName = url.getParameter("key1", url.getParameter("key2", url.getParameter("key3", "a")))。
- 利用【ExtensionLoader】获取扩展名对应的实现类实例。
- 调用实际实例的方法,返回结果。
public class ExtensionLoader<T> {
//笔者注 获取SPI接口对应的Adaptive类的实例,先从缓存中取,没有则自动生成一个Adaptive类创建实例
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;
}
@SuppressWarnings("unchecked")
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;
}
//笔者注 没有则自动生成一个Adaptive类
return cachedAdaptiveClass = createAdaptiveExtensionClass();
}
private Class<?> createAdaptiveExtensionClass() {
//笔者注 自动生成一个Adaptive类
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);
}
}
public class AdaptiveClassCodeGenerator {
private static final String CODE_CLASS_DECLARATION = "public class %s$Adaptive implements %s {\n";
private static final String CODE_METHOD_DECLARATION = "public %s %s(%s) %s {\n%s}\n";
private static final String CODE_METHOD_ARGUMENT = "%s arg%d";
private static final String CODE_METHOD_THROWS = "throws %s";
private static final String CODE_UNSUPPORTED = "throw new UnsupportedOperationException(\"The method %s of interface %s is not adaptive method!\");\n";
private static final String CODE_URL_NULL_CHECK = "if (arg%d == null) throw new IllegalArgumentException(\"url == null\");\n%s url = arg%d;\n";
private static final String CODE_EXT_NAME_ASSIGNMENT = "String extName = %s;\n";
//笔者注 生成Adaptive类,拼接类的字符串,然后编译加载
public String generate() {
// no need to generate adaptive class since there's no adaptive method found.
if (!hasAdaptiveMethod()) {
throw new IllegalStateException("No adaptive method exist on extension " + type.getName() + ", refuse to create the adaptive class!");
}
StringBuilder code = new StringBuilder();
code.append(generatePackageInfo());
code.append(generateImports());
code.append(generateClassDeclaration());
Method[] methods = type.getMethods();
for (Method method : methods) {
code.append(generateMethod(method));
}
code.append("}");
if (logger.isDebugEnabled()) {
logger.debug(code.toString());
}
return code.toString();
}
}
- List<T> getActivateExtension(URL url, String[] values, String group) 实例方法 获取激活的实现类实例,values是扩展名列表("-name"表示剔除该扩展名对应的实现类)、group一般就是provider(提供方)、consumer(消费方)
逻辑如下
- 如果不剔除默认(即values[]没有"-default")则先收集[默认激活实例];
- 依次获取values中实现类的实例("-name"表示剔除name对应的实例,即不获取);
- 根据"default"在values位置确定[默认激活实例]的位置,如果没有"default"则表示[默认激活实例]在最前面,values[]里面对应的实例依次放在后面;
public class ExtensionLoader<T> {
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> activateExtensions = new ArrayList<>();
List<String> names = values == null ? new ArrayList<>(0) : asList(values);
//笔者注 如果不剔除默认(即没有"-default")则先收集[默认激活实例]
//[默认激活实例]有几个特点
//(1)实现类上有@Activate注解且跟group和url匹配
//(2)实现类扩展名不在values中("-name"或name)
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)) {
activateExtensions.add(getExtension(name));
}
}
activateExtensions.sort(ActivateComparator.COMPARATOR);
}
//笔者注 依次获取values中实现类的实例(-name表示剔除,不获取)。根据"default"位置决定默认[激活实例]位置
List<T> loadedExtensions = new ArrayList<>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(REMOVE_VALUE_PREFIX)
&& !names.contains(REMOVE_VALUE_PREFIX + name)) {
//笔者注 如果是"default"说明之前的实现类实例,要放在[默认激活实例]之前
if (DEFAULT_KEY.equals(name)) {
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(0, loadedExtensions);
loadedExtensions.clear();
}
} else {
loadedExtensions.add(getExtension(name));
}
}
}
//其余的 放在[默认激活实例]之后
if (!loadedExtensions.isEmpty()) {
activateExtensions.addAll(loadedExtensions);
}
return activateExtensions;
}
}
2 SPI在dubbo中应用
【Dubbo】广泛使用SPI机制,所有@SPI注解的接口都是一个SPI扩展点,如下图:
3 示例
1 首先定义一个@SPI注解的接口,默认实现类是"a"
package com.focuse.jdkdemo.dubbospi;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.Adaptive;
import org.apache.dubbo.common.extension.SPI;
@SPI("a")
public interface DubboSpiTest {
@Adaptive
String read(URL url);
void write(String a);
}
2 定义2个实现类DubboSpiTestImplA、DubboSpiTestImplB
package com.focuse.jdkdemo.dubbospi;
import org.apache.dubbo.common.URL;
public class DubboSpiTestImplA implements DubboSpiTest{
public String read(URL url) {
return "Hello A!!";
}
public void write(String a){
}
}
package com.focuse.jdkdemo.dubbospi;
import org.apache.dubbo.common.URL;
public class DubboSpiTestImplB implements DubboSpiTest{
public String read(URL url) {
return "Hello B!!";
}
public void write(String a){
}
}
3 创建spi文件 META-INF/dubbo/com.focuse.jdkdemo.dubbospi.DubboSpiTest
a=com.focuse.jdkdemo.dubbospi.DubboSpiTestImplA
b=com.focuse.jdkdemo.dubbospi.DubboSpiTestImplB
4 ExtensinoLoader获取DubboSpiTest的Adaptive类的实例
package com.focuse.jdkdemo.dubbospi;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;
import java.util.HashMap;
/**
* @author :
* @date :Created in 2020/6/6 下午3:08
* @description:
* @modified By:
*/
public class DubboSpiApplication {
public static void main(String[] args) throws Exception{
//获取DubboSpiTest接口的adaptive类
DubboSpiTest dubboSpiTest = ExtensionLoader.getExtensionLoader(DubboSpiTest.class).getAdaptiveExtension();
HashMap<String, String> params = new HashMap<>();
//上下文中指定实现类,因为DubboSpiTest.read方法上注解@Adaptive没有value值,
//所以上下文的key就是DubboSpiTest类名(驼峰转xxx.yyy.zz形式)
params.put("dubbo.spi.test", "b");
URL context = new URL("dubbo", "DubboSpiTest", 0, params);
String result = dubboSpiTest.read(context);
System.out.println("*************************");
System.out.println(result);
System.out.println("*************************");
}
}
在上下文中指定类实现类用"b"(因为DubboSpiTest.read方法上注解@Adaptive没有value值,所以上下文的key就是DubboSpiTest类名(驼峰转xxx.yyy.zz形式)),所以实际调用的是DubboSpiTestImplB.read()
如果不指定,则会使用默认的"a"
DubboSpiTest$Adaptive是自动生成的DubboSpiTest的Adaptive类,扩展名先从上下文中取,没有则用默认的"a",拿到扩展名后再获取扩展名对应的实例。
package com.focuse.jdkdemo.dubbospi;
import org.apache.dubbo.common.extension.ExtensionLoader;
public class DubboSpiTest$Adaptive implements com.focuse.jdkdemo.dubbospi.DubboSpiTest {
public void write(java.lang.String arg0) {
throw new UnsupportedOperationException("The method public abstract void com.focuse.jdkdemo.dubbospi.DubboSpiTest.write(java.lang.String) of interface com.focuse.jdkdemo.dubbospi.DubboSpiTest is not adaptive method!");
}
public java.lang.String read(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
String extName = url.getParameter("dubbo.spi.test", "a");
if (extName == null)
throw new IllegalStateException("Failed to get extension (com.focuse.jdkdemo.dubbospi.DubboSpiTest) name from url (" + url.toString() + ") use keys([dubbo.spi.test])");
com.focuse.jdkdemo.dubbospi.DubboSpiTest extension = (com.focuse.jdkdemo.dubbospi.DubboSpiTest) ExtensionLoader.getExtensionLoader(com.focuse.jdkdemo.dubbospi.DubboSpiTest.class).getExtension(extName);
return extension.read(arg0);
}
}