可能有的同学要问了,分析dubbo源码为啥都要从spi开始。其实答案很简单----》你不明白这个,你看不懂dubbo的调用流程啊!dubbo里大量使用的适配器扩展动态生成适配器类,你看不懂这个,你跟不不知道dubbo的流程时怎么走的(有点吹啊,你能懂算你🐂!)
一 java的spi机制
说白了,就是你需要在META-INF/services/这个目录下写个配置文件,配置文件名字是你要扩展的接口名,文件内容就是此接口的实现类全名,可以写多个,然后呢jdk帮你写了个java.util.ServiceLoader类通过反射机制找到这个文件,实例化文件里的所有实现类(具体内容大家自己查查就知道了)。
如文件名:
文件内容:
我用的时候:
就这么简单,但明显这中模式有一些问题,啥问题?
1.每次加载会把所有类加载,有用没用都给我加载了
2.我如果想找某一个类,需要遍历所有找到匹配的那个很麻烦。
等等。。。。
然后dubbo就自己基于此原理开发了一套spi扩展机制
1 先总体看他的文件是这样子的:
文件内容是这样子的:
我们发现他只是在文件内容中多了个名字,想必如果让我们做,这个名字应该是来做缓存的(其实他也是干这个用的)
2 让我们看看dubbo是怎么写的:
他的入口类为ExtensionLoader,我们先看看他都有哪些属性:
//配置文件位置1,沿用java的目录
private static final String SERVICES_DIRECTORY = "META-INF/services/";
//配置文件位置
private static final String DUBBO_DIRECTORY = "META-INF/dubbo/";
//配置文件位置2,dubbo自定义的目录
private static final String DUBBO_INTERNAL_DIRECTORY = DUBBO_DIRECTORY + "internal/";
private static final Pattern NAME_SEPARATOR = Pattern.compile("\\s*[,]+\\s*");
//用来缓存每个类对应的ExtensionLoader加载器
private static final ConcurrentMap<Class<?>, ExtensionLoader<?>> EXTENSION_LOADERS = new ConcurrentHashMap<Class<?>, ExtensionLoader<?>>();
private static final ConcurrentMap<Class<?>, Object> EXTENSION_INSTANCES = new ConcurrentHashMap<Class<?>, Object>();
// ==============================
//用来缓存此加载器对应的类型
private final Class<?> type;
//缓存对应ExtensionFactory工厂,后续注入逻辑会用到
private final ExtensionFactory objectFactory;
//缓存类型对配置文件中名字对映射关系
private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
//缓存名字对应类型,和cachedNames相呼应,想成双向
private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
//缓存配置文件中name对应对实例中哪些是Activate对
private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
//缓存已加载对name对应对实例
private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
//缓存生成自适应实例
private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
//缓存对应对自适应类
private volatile Class<?> cachedAdaptiveClass = null;
//缓存对应对default名字
private String cachedDefaultName;
//存对应建设自适应类时产生错误
private volatile Throwable createAdaptiveInstanceError;
//缓存配置文件中对应类对所有时wrapper对类型
private Set<Class<?>> cachedWrapperClasses;
看完这些属性(请记住注解对内容),下面开始进入正题,我们会举例说话:
例 Protocol adaptiveExtension = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension()
可以看出代码意思是加载Protocol类型对所有实现,并找到对应Adaptive类型扩展点,dubbo对spi中有几个注解:
- SPI:表示当前接口为扩展点,可以指定value,表示默认扩展点对应名
- Adaptive:表示此类型扩展点的自适应对象,有方法和类两种层级定义(后面会详细说明)
- Activate:表示此扩展点为自动激活型,dubbo会自动帮你加载
进入代码,按习惯先来张大图:
由图中我们可以看到第一步当没有时会new个ExtensionLoader,
@SuppressWarnings("unchecked")
public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
....
//一个type对应一个loader类,先从缓存中查看没有大化new个新的
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;
}
注意他在new的时候还有点小动作,赋值了个objectFactory,这是干嘛用的呢?留下疑问🤔️,后面解答(当然大图里已有答案)。
//new 个ExtensionLoader
private ExtensionLoader(Class<?> type) {
this.type = type;
//如果当前不是ExtensionFactory工厂则赋值
objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExension());
}
根据流转图,下面开始调用,缓存部分略过,然后我们重点进入createAdaptiveExtension();
public T getAdaptiveExtension() {
//类型自适应缓存实例
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("fail to create adaptive instance: " + t.toString(), t);
}
}
}
} else {
throw new IllegalStateException("fail to create adaptive instance: " + createAdaptiveInstanceError.toString(), createAdaptiveInstanceError);
}
}
return (T) instance;
}
明显createAdaptiveExtension();会生成我们最终要的实例,里边主要逻辑落到了这里:
return injectExtension((T) getAdaptiveExtensionClass().newInstance());
根据流程图,我们首先分析getAdaptiveExtensionClass(),通过名字我们看是加载对应的自适应的class,缓存部分略过,流程图显示重点逻辑进入getExtensionClasses中然后核心逻辑分为两个分支,一个loadExtensionClasses表示配置文件中加载所有可用实例的class,另一个在没有找到自适应类的情况下通过createAdaptiveExtensionClass()方法的字节码技术生成一个自适应类(这是一个重点)。
先看loadExtensionClasses:
// 此方法已经getExtensionClasses方法同步过。
private Map<String, Class<?>> loadExtensionClasses() {
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if (value != null && (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));
}
//spi配置对应的默认名字
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//加载三个配置文件中的对应类的所有类
loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
loadFile(extensionClasses, DUBBO_DIRECTORY);
loadFile(extensionClasses, SERVICES_DIRECTORY);
return extensionClasses;
}
再看loadFile明显是去加载上面说的配置文件了,但其中有几个小动作我们要注意下:
private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
....
//配置文件中的name和className
name = line.substring(0, i).trim();
line = line.substring(i + 1).trim();
....
Class<?> clazz = Class.forName(line, true, classLoader);
.....
if (clazz.isAnnotationPresent(Adaptive.class)) {
....
//对应上面说的Adaptive注解的类级别,直接赋值
cachedAdaptiveClass = clazz;
} else {
try {
//重点逻辑,如果此类为wrapper类,将在此缓存
clazz.getConstructor(type);
Set<Class<?>> wrappers = cachedWrapperClasses;
if (wrappers == null) {
cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
wrappers = cachedWrapperClasses;
}
wrappers.add(clazz);
} catch (NoSuchMethodException e) {
....
clazz.getConstructor();
....
//不是wrapper类的缓存到扩张集合中,名字也缓存了
cachedNames.put(clazz, n);
....
extensionClasses.put(n, clazz);
}
}
上面的wrapper那段要注意,这段逻辑是形成filer模式的基础,我们回过看下配置文件就会发现这种对象:
加载完配置文件中的所有class这个时候如果发现没有类级别的Adaptive注解,我们将会走到createAdaptiveExtensionClass这个重点分支(这也是dubbo扩展设计牛逼的一个地方):
private Class<?> createAdaptiveExtensionClass() {
//生成自适应类文件内容
String code = createAdaptiveExtensionClassCode();
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);
}
明显他是通过java的Compiler功能动态生成了class,所以我们重点看的应该就是code是什么(有没了解Compiler机制的小朋友,可以自己学习下啊),我通过debug的方式获取此时的code如下:
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.Invoker refer(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);
}
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])");
//根据url中的属性来下适配下一个扩展点
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);
}
}
看这就是dubbo最终动态生成的自适应,后面我们将会用到,现在先不解释,但有个坑得在此出填一下,就是Adaptive刚才讲了类级别注解,那方法级别呢?对了,就是这时候用的,一言不合上代码:
private String createAdaptiveExtensionClassCode() {
....
for (Method method : methods) {
Class<?> rt = method.getReturnType();
Class<?>[] pts = method.getParameterTypes();
Class<?>[] ets = method.getExceptionTypes();
Adaptive adaptiveAnnotation = method.getAnnotation(Adaptive.class);
StringBuilder code = new StringBuilder(512);
//对应的自适应注解的方法此处会重点逻辑
if (adaptiveAnnotation == null) {
code.append("throw new UnsupportedOperationException(\"method ")
.append(method.toString()).append(" of interface ")
.append(type.getName()).append(" is not adaptive method!\");");
} else {
....
}
}
codeBuidler.append("\n}");
if (logger.isDebugEnabled()) {
logger.debug(codeBuidler.toString());
}
return codeBuidler.toString();
}
诶,我们发现dubbo实现的时候非自适应的被一个异常带过了,非自适应的我们看了边生成类可以看出,大同小异,都是根据url中的属性来下适配下一个扩展点,发布服务时我们会来揭晓他的使用。
至此我们需要的getAdaptiveExtension就获取到了class,下面根据大图流程就new对象,(T) getAdaptiveExtensionClass().newInstance(),这就没啥说的了,然后就是injectExtension()方法,这也是dubbo的spi一个重点方法,
private T injectExtension(T instance) {
try {
if (objectFactory != null) {
for (Method method : instance.getClass().getMethods()) {
if (method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& Modifier.isPublic(method.getModifiers())) {
Class<?> pt = method.getParameterTypes()[0];
try {
String property = method.getName().length() > 3 ? method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4) : "";
//获取对应有set方法的属性从objectFactory中进行赋值
Object object = 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;
}
这段逻辑和spring中的依赖注入有异曲同工之妙,那dubbo又是如何spring挂钩的呢?那又回到了objectFactory的生成,上文已经说过objectFactory是通过
ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExension())获取到的自适应类,先让我们看这个类什么样子,我们找到com.alibaba.dubbo.common.extension.ExtensionFactory对应的配置文件中有三个类,其中有个AdaptiveExtensionFactory为@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);
}
//获取属性方法,从所有工厂获取
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;
}
}
可以看出这个适配器类中其实是从另两个扩展工厂去找对象到,其中就有spring工厂
好了,到这里加载自适应到相关主逻辑就看完了,让我们歇口气,看看下一个spi中到知识点
例 getExtension(String name)方法(这个就简单了):
public T getExtension(String name) {
if (name == null || name.length() == 0)
throw new IllegalArgumentException("Extension name == null");
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = 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();
if (instance == null) {
//获取扩展点
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
这里边重点createExtension(name)方法,可以看到上面加载class一样,重点是最后多了个包装多逻辑,这就是filer形成多源头,后面会有大用
private T createExtension(String name) {
//已经分析过的加载class逻辑
Class<?> clazz = getExtensionClasses().get(name);
.....
//包装类包装逻辑
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && wrapperClasses.size() > 0) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
....
}
over!到此dubbo的spi实现大致分析完了,但你可能对其使用还是没有了解清楚,那就得看看下面分析但dubbo服务发布、引用和调用等等逻辑了,毕竟只有用过的东西才代表你会了。
下一篇 dubbo服务发布源码欣赏
首页 dubbo源码欣赏简