Skip to the content.

1-1 获取扩展类实现

上一篇文章已经介绍过了,Dubbo通过ExtensionLoader.getExtension(String name)根据实现类名称获取具体的扩展类实现,其中真正创建扩展类实现的方法是createExtension(String name)

源码如下:

private T createExtension(String name) {
    // 获取所有扩展类Class对象
    // 根据name获取Class对象
    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;
        // 使用Wrapper类包装
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                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);
    }
}

该方法主要进行了如下6步处理:

  1. 获取所有的扩展类Class对象
  2. 根据name获取具体的扩展类Class对象
  3. 通过扩展类Class对象获取缓冲中已实例化过的扩展类对象
  4. 如果没获取到,则通过反射实例化扩展类对象
  5. 对扩展类对象进行依赖注入
  6. 通过包装类包装扩展类对象(装饰器模式)。

这里笔者主要对1、5这两个步骤进行分析,分析完这两个步骤,读者应该就了解Dubbo的SPI是如何解决实例化只能简单通过反射创建,如果要对创建好的对象进行二次包装,只能用户手动完成。这个问题的了。

在分析完这两个步骤之后,为了使读者阅读起来没有那么突兀,笔者会再次分析createExtension(String name)这个方法的整体处理逻辑。

1.1 获取所有扩展类的Class对象

Dubbo的SPI配置文件也可以放在META-INF/services目录下,除此之外,还可以放在其他的地方,例如: META-INF/dubbo/,SPI模块就是对这两个位置的配置文件进行解析,然后获取所有的扩展类的Class对象的。

具体的处理方法就是getExtensionClasses()方法,源码如下:

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;
}

Dubbo为扩展类的解析也创建了一个缓存cachedClasses用于存储解析结果(一个key为String,value为Class类型的数组),毕竟这是文件读取操作,而且配置文件是不会动态改变的。真正解析配置的方法是loadExtensionClasses()方法

private Map<String, Class<?>> loadExtensionClasses() {
    cacheDefaultExtensionName();

    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;
}

该方法主要完成了下面两步操作:

  1. 通过cacheDefaultExtensionName()方法获取当前接口的默认扩展名。
  2. 通过类加载器加载所有配置文件中的扩展类。

Dubbo的SPI模块使用接口上的@SPI注解指定默认实现扩展名称,例如Dubbo协议名默认为dubboorg.apache.dubbo.rpc.Protocol接口源码如下:

@SPI("dubbo")
public interface Protocol {}

cacheDefaultExtensionName()方法正是读取这个注解,获取的默认扩展名称:

private void cacheDefaultExtensionName() {
    final SPI defaultAnnotation = type.getAnnotation(SPI.class);
    if (defaultAnnotation != null) {
        String value = defaultAnnotation.value();
        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) {
                cachedDefaultName = names[0];
            }
        }
    }
}

cacheDefaultExtensionName()还处理了@SPI注解中的名称被,分隔的情况,默认采用第一个名称。

在获取了接口的默认扩展后,通过loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type)方法Dubbo加载了所有扩展类的Class对象,配置文件所在目录为:

在调用loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type)方法时有的进行了类名更换操作,例如:

loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba"));

这是因为Dubbo在未开源到Apache,默认使用的包名是com.alibaba。加载扩展实现类分3个步骤:

  1. 获得类加载器,加载目录:loadDirectory
     private void loadDirectory(Map<String, Class<?>> extensionClasses, String dir, String type) {
         // 文件名
         String fileName = dir + type;
         try {
             Enumeration<java.net.URL> urls;
             // 获取类加载器
             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);
         }
     }
    
  2. 解析配置文件:loadResource
     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;
                             int i = line.indexOf('=');
                             if (i > 0) {
                                 name = line.substring(0, i).trim();
                                 line = line.substring(i + 1).trim();
                             }
                             if (line.length() > 0) {
                                 // 根据类名加载实现类
                                 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);
         }
     }
    
  3. 加载扩展类Class文件: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)) {
             // 指定了自适应扩展,不需要自动生成代码
             // 修改cachedAdaptiveClass
             cacheAdaptiveClass(clazz);
         } else if (isWrapperClass(clazz)) {
             // 是装饰器类
             // 修改cachedWrapperClasses
             cacheWrapperClass(clazz);
         } else {
             // 单纯的扩展类
             // 获取无参构造器,没有就会抛出异常
             clazz.getConstructor();
             if (StringUtils.isEmpty(name)) {
                 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);
             // 尝试修改cachedActivates维护激活点
             // 尝试维护缓冲实例名
             // 尝试维护扩展类名
             if (ArrayUtils.isNotEmpty(names)) {
                 cacheActivateClass(clazz, names[0]);
                 for (String n : names) {
                     cacheName(clazz, n);
                     saveInExtensionClass(extensionClasses, clazz, name);
                 }
             }
         }
     }
    

    通过loadClass方法,将扩展实现分为4类:

    1. 自适应扩展类:不需要自动生成代码,典型代表ExtensionFactory
    2. 装饰器类:构造器是要求接口类型的类
    3. 带激活点的类:具有无参构造器,被Adaptive修饰的类
    4. 通常类:具有无参构造器的类

Dubbo通过包装类实现了对扩展实现进行类似于AOP的包装,但Dubbo要求包装类必须使用装饰器模式。

1.2 依赖注入

通过getExtensionClasses()方法,Dubbo将扩展实现分为4类:

  1. 自适应扩展类:不需要自动生成代码,典型代表ExtensionFactory
  2. 装饰器类
  3. 带激活点的类
  4. 通常实现类

由装饰器类帮助对剩下三种进行包装,但是某些扩展实现需要其他的Bean作为依赖,因此,Dubbo提供了自动注入的功能,该功能由injectExtension(T instance)方法实现:

private T injectExtension(T instance) {
    try {
        if (objectFactory != null) {
            for (Method method : instance.getClass().getMethods()) {
                if (isSetter(method)) {
                    // 被DisableInject修饰,则不进行自动注入
                    if (method.getAnnotation(DisableInject.class) != null) {
                        continue;
                    }
                    // 如果方法的第一个参数是基本类型,则跳过
                    Class<?> pt = method.getParameterTypes()[0];
                    if (ReflectUtils.isPrimitives(pt)) {
                        continue;
                    }
                    try {
                        // 判断是否是setter方法
                        // 获取参数名
                        String property = getSetterProperty(method);
                        // 根据参数名寻找对应扩展实现类
                        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;
}

通过injectExtension方法中的依赖注入可以看到,进行依赖注入时,bean都是通过objectFactoryExtensionFactory类获取的。

1.3 回顾createExtension

前面两个主要的方法分析完毕了,我们再次回到createExtension方法:

private T createExtension(String name) {
    // 获取所有扩展类Class对象
    // 根据name获取Class对象
    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;
        // 使用Wrapper类包装
        if (CollectionUtils.isNotEmpty(wrapperClasses)) {
            for (Class<?> wrapperClass : wrapperClasses) {
                // 依赖注入包装器
                // 使用装饰器封装类型实现
                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);
    }
}

总结