1. 写在前面:
读dubbo源码之前建议先看看官方文档http://dubbo.apache.org/zh-cn/docs/user/quick-start.html,很少有人能比原作者们写的更好了,只能说是按自己的理解来阐述。
2. spi你想干啥子
编程中我们有一般遵循几大原则比如开闭原则、单一职责、依赖倒置等等。依赖倒置核心思想是:要面向接口编程,不要面向实现编程。
SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。看到很多文章说SPI是java提供的一种机制,我认为不如说是jdk提供了SPI的一个具体实现,正如Spring与IOC、AOP的关系一样。在 Dubbo 中,SPI 是一个非常重要的模块。基于 SPI,我们可以很容易的对 Dubbo 进行拓展。如果大家想要学习 Dubbo 的源码,SPI 机制务必弄懂。
spring也有spi的实现,在自动注入查找接口实现类也需要用的这个能力,比如有多个实现类的时候不进行具体指定IOC容器会启动报错,但这不是本文的讨论重点,我们回到JDK和dubbo的实现上
3. jdk、dubbo你们家叫spi的闺女长得漂亮不
例子的来源仍然是dubbo官网的源码分析部分,记录一下实现细节
3.1 配置准备
在测试包下分别添加测试类与jdk(META-INF/services)、dubbo(META-INF/dubbo)的配置文件名为接口全类名,配置文件中记录实现类信息,测试类中获取具体实现。
jdk spi记录实现类全类名,路径:META-INF/services
com.example.productservice.service.OptimusPrime
com.example.productservice.service.Bumblebee
dubbo spi记录信息,路径:META-INF/dubbo
optimusPrime=com.example.productservice.service.OptimusPrime
bumblebee=com.example.productservice.service.Bumblebee
两者记录信息不同和具体实现有关系,下面是测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProductServiceApplicationTests {
/**
* jdk spi
*/
@Test
public void contextLoads() {
ServiceLoader<Robot> serviceLoader = ServiceLoader.load(Robot.class);
System.out.println("Java SPI");
serviceLoader.forEach(Robot::sayHello);
}
/**
* dubbo spi
*/
@Test
public void sayHello() {
ExtensionLoader<Robot> extensionLoader =
ExtensionLoader.getExtensionLoader(Robot.class);
Robot optimusPrime = extensionLoader.getExtension("optimusPrime");
optimusPrime.sayHello();
Robot bumblebee = extensionLoader.getExtension("bumblebee");
bumblebee.sayHello();
}
}
4. spi你过来,我们好好谈谈
前面看过了spi都是些什么,接下来聊聊Dubbo的spi实现原理。其实最好的方式当然还是官方文档结合源码来看了http://dubbo.apache.org/zh-cn/docs/source_code_guide/dubbo-spi.html,如前文所描述下面的内容是笔者消化后打的饱嗝
4.1 从getExtension说起
我们首先根据官网文档找到SPI的入口org.apache.dubbo.common.extension.ExtensionLoader
,下面的函数会加载给定的扩展,没找到就抛异常(注释说明)。整个代码的逻辑还是比较简单,首先检查缓存缓存未命中则创建拓展对象。
/**
* 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) {
//校验入参合规
if (StringUtils.isEmpty(name)) {
throw new IllegalArgumentException("Extension name == null");
}
//
if ("true".equals(name)) {
return getDefaultExtension();
}
Holder<Object> holder = getOrCreateHolder(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;
}
以上代码有3块封装的功能,getDefaultExtension()
,getOrCreateHolder(String name)
,createExtension(String name)
需要说明。前两者比较简单,创建扩展实例比较重要。
-
先说
getOrCreateHolder(String name)
,获取或创建holder,其实holder也很简单,只是统一持有了对象private Holder<Object> getOrCreateHolder(String name) { //缓存map(ConcurrentHashMap)中获取holder Holder<Object> holder = cachedInstances.get(name); //未获取到就new一个 if (holder == null) { cachedInstances.putIfAbsent(name, new Holder<>()); holder = cachedInstances.get(name); } return holder; } /** * Helper Class for hold a value. */ public class Holder<T> { private volatile T value; public void set(T value) { this.value = value; } public T get() { return value; } }
-
再说
getOrCreateHolder(String name)
,它又有几个调用,不过逻辑上都很简单,我们一个个来看/** * Return default extension, return <code>null</code> if it's not configured. */ public T getDefaultExtension() { //1.获取扩展classes getExtensionClasses(); //2.如果没有配置,return null if (StringUtils.isBlank(cachedDefaultName) || "true".equals(cachedDefaultName)) { return null; } //3.如果有,获取默认扩展 return getExtension(cachedDefaultName); } private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<>(); //获取扩展classes private Map<String, Class<?>> getExtensionClasses() { //cachedClasses在了上面,是一个持有String-Class类型k-v map的Holder Map<String, Class<?>> classes = cachedClasses.get(); //双重检测,并发处理,加载扩展类 if (classes == null) { synchronized (cachedClasses) { classes = cachedClasses.get(); if (classes == null) { //加载扩展类 classes = loadExtensionClasses(); cachedClasses.set(classes); } } } return classes; } private static final String SERVICES_DIRECTORY="META-INF/services/"; private static final String DUBBO_DIRECTORY="META-INF/dubbo/"; private static final String DUBBO_INTERNAL_DIRECTORY=DUBBO_DIRECTORY+"internal/"; // synchronized in getExtensionClasses private Map<String, Class<?>> loadExtensionClasses() { //1. 加载缓存默认的扩展名 cacheDefaultExtensionName(); //2. 加载缓存配置的扩展名 Map<String, Class<?>> extensionClasses = new HashMap<>(); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); return extensionClasses; } /** * extract and cache default extension name if exists */ private void cacheDefaultExtensionName() { // 获取 SPI 注解,这里的 type 变量是在调用 getExtensionLoader 方法时传入的 final SPI defaultAnnotation = type.getAnnotation(SPI.class); if (defaultAnnotation != null) { String value = defaultAnnotation.value(); if ((value = value.trim()).length() > 0) { // 对 SPI 注解内容进行切分 String[] names = NAME_SEPARATOR.split(value); // 检测 SPI 注解内容是否合法,不合法则抛出异常 if (names.length > 1) { throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + ": " + Arrays.toString(names)); } // 设置默认名称 if (names.length == 1) { cachedDefaultName = names[0]; } } } } //loadDirectory 方法先通过 classLoader 获取所有资源链接url,然后再通过 loadResource 方法加载资源。 private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) { // fileName = 文件夹路径 + type 全限定名 String fileName = dir + type; try { Enumeration<java.net.URL> urls; //获取加载ExtensionLoader.class的ClassLoader ClassLoader classLoader = 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(); // 加载资源 loadResource(extensionClasses, classLoader, resourceURL); } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", description file: " + fileName + ").", t); } } //loadResource 方法用于读取和解析配置文件,并通过反射加载类,最后调用 loadClass 方法进行其他操作 private void loadResource(Map<String, Class<?>> extensionClasses, ClassLoader classLoader, java.net.URL resourceURL) { try { try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) { String line; // 按行读取配置内容 while ((line = reader.readLine()) != null) { // 定位 # 字符 final int ci = line.indexOf('#'); if (ci >= 0) { // 截取 # 之前的字符串,# 之后的内容为注释,需要忽略 line = line.substring(0, ci); } line = line.trim(); if (line.length() > 0) { try { String name = null; // 以等于号 = 为界,截取键与值,这体现到了dubbo的spi配置 int i = line.indexOf('='); if (i > 0) { name = line.substring(0, i).trim(); line = line.substring(i + 1).trim(); } if (line.length() > 0) { // 加载类,并通过 loadClass 方法对类进行缓存 loadClass(extensionClasses, resourceURL, Class.forName(line, true, classLoader), name); } } catch (Throwable t) { IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t); exceptions.put(line, e); } } } } } catch (Throwable t) { logger.error("Exception occurred when loading extension class (interface: " + type + ", class file: " + resourceURL + ") in " + resourceURL, t); } }
-
loadClass我们单独来说
//loadClass 方法用于主要用于操作缓存 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 occurred when loading extension class (interface: " + type + ", class line: " + clazz.getName() + "), class " + clazz.getName() + " is not subtype of interface."); } if (clazz.isAnnotationPresent(Adaptive.class)) { // 检测目标类上是否有 Adaptive 注解,完成缓存 cacheAdaptiveClass(clazz); } else if (isWrapperClass(clazz)) { // 检测 clazz 是否是 Wrapper 类型,完成缓存 cacheWrapperClass(clazz); } else {// 程序进入此分支,表明 clazz 是一个普通的拓展类 // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常 clazz.getConstructor(); if (StringUtils.isEmpty(name)) { // 如果name为空,则尝试从Extension注解中获取 name,或使用小写的类名作为name name = findAnnotationName(clazz); if (name.length() == 0) { throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); } } // 切分 name String[] names = NAME_SEPARATOR.split(name); if (ArrayUtils.isNotEmpty(names)) { // 设置 cachedAdaptiveClass缓存 cacheActivateClass(clazz, names[0]); for (String n : names) { cacheName(clazz, n); saveInExtensionClass(extensionClasses, clazz, name); } } } } /** * cache Adaptive class which is annotated with <code>Adaptive</code> */ private void cacheAdaptiveClass(Class<?> clazz) { if (cachedAdaptiveClass == null) { cachedAdaptiveClass = clazz; } else if (!cachedAdaptiveClass.equals(clazz)) { throw new IllegalStateException("More than 1 adaptive class found: " + cachedAdaptiveClass.getClass().getName()+ ", " + clazz.getClass().getName()); } } /** * cache Activate class which is annotated with <code>Activate</code> * for compatibility, also cache class with old alibaba Activate annotation */ private void cacheActivateClass(Class<?> clazz, String name) { Activate activate = clazz.getAnnotation(Activate.class); if (activate != null) { // 如果类上有 Activate 注解,则使用 names 数组的第一个元素作为键, // 存储 name 到 Activate 注解对象的映射关系 cachedActivates.put(name, activate); } else { // support com.alibaba.dubbo.common.extension.Activate com.alibaba.dubbo.common.extension.Activate oldActivate = clazz.getAnnotation(com.alibaba.dubbo.common.extension.Activate.class); if (oldActivate != null) { cachedActivates.put(name, oldActivate); } } } /** * cache wrapper class * like: ProtocolFilterWrapper, ProtocolListenerWrapper */ private void cacheWrapperClass(Class<?> clazz) { if (cachedWrapperClasses == null) { cachedWrapperClasses = new ConcurrentHashSet<>(); } cachedWrapperClasses.add(clazz); } /** * cache name */ private void cacheName(Class<?> clazz, String name) { if (!cachedNames.containsKey(clazz)) { cachedNames.put(clazz, name); } }
到这里整个默认extension的过程就完成了
-
-
最后来看看
createExtension(String name)
,这里涉及一些dubbo IOC的内容,一起来看看private T createExtension(String 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)) { // 循环创建 Wrapper 实例 for (Class<?> wrapperClass : wrapperClasses) { //将当前instance作为参数传给Wrapper的构造方法,并通过反射创建 Wrapper 实例 //然后向Wrapper实例中注入依赖,最后将Wrapper实例再次赋值给 instance 变量 instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));//完成包装与依赖注入 } } return instance; } catch (Throwable t) { throw new IllegalStateException("Extension instance (name: " + name + ", class: " + type + ") couldn't be instantiated: " + t.getMessage(), t); } } private T injectExtension(T instance) { try { if (objectFactory != null) { // 遍历目标类的所有方法 for (Method method : instance.getClass().getMethods()) { // 检测方法是否以 set 开头,且方法仅有一个参数,且方法访问级别为 public if (isSetter(method)) { //检测是否关闭依赖注入 if (method.getAnnotation(DisableInject.class) != null) { continue; } // 获取 setter 方法参数类型 Class<?> pt = method.getParameterTypes()[0]; if (ReflectUtils.isPrimitives(pt)) { continue; } try { // 获取属性名,比如 setName 方法对应属性名 name String property = getSetterProperty(method); // 从 ObjectFactory 中获取依赖对象 Object object = objectFactory.getExtension(pt, property); if (object != null) { // 通过反射调用 setter 方法设置依赖 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; }
objectFactory的实现类SpiExtensionFactory 和 SpringExtensionFactory关于getExtension的实现并不复杂spring是通过
context.getBean
获取。spiExtension是通过缓存cachedAdaptiveInstance获取,获取不到就通过反射创建一个