一、 Java SPI(Service Provider Interface)

1.SPI思想

具有可插拔性,在软件系统的设计中,可插拔是一个重要特性。它意味着给系统添加新功能的时候(或者将原来功能的实 现替换成新的实现而保持接口不变),不改变系统已有的功能。

我们系统里抽象的各个模块,往往有很多不同的实现方案,比如日志模块的方案,xml解析模块、jdbc模块的方案等。 面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类, 就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候能不在程序里动态指明, 这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。

2.JAVA中SPI的约定

当服务的提供者,提供了服务接口的一种实现之后,在jar包的META-INF/services/目录里同时创建一个以服务接口命名的文件。 该文件里就是实现该服务接口的具体实现类。而当外部程序装配这个模块的时候,就能通过该jar包META-INF/services/里的配置 文件找到具体的实现类名,并装载实例化,完成模块的注入。基于这样一个约定就能很好的找到服务接口的实现类,而不需要再代码 里制定。jdk提供服务实现查找的一个工具类:java.util.ServiceLoader.

二、Duboo中spi实现

1.dubbo中基于SPI思想的实现

Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来。

Dubbo改进了JDK标准的SPI的以下问题:

  • JDK标准的SPI会ServiceLoader迭代实例化扩展类,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
  • 如果扩展点加载失败,连扩展点的名称都拿不到了。 比如:JDK标准的ScriptEngine,通过getName();获取脚本类型的名称,但如果RubyScriptEngine因为所依赖的jruby.jar不存在, 导致RubyScriptEngine类加载失败,这个失败原因被吃掉了,和ruby对应不起来,当用户执行ruby脚本时,会报不支持ruby,而不是真正失败的原因。
  • 扩展类不能进行setter注入,不支持AOP和IOC支持。

2.Doubbo中SPI的约定

当服务的提供者,提供了服务接口的一种实现之后,在jar包的### META-INF/dubbo/internal/,META-INF/dubbo/ 和META-INF/services/三个目录中里同时创建一个以服务接口命名的文件。com.alibaba.dubbo.common.extension.ExtensionLoader 提供服务实现查找的一个工具类

3.说一说注解

  • @SPI在接口类,配置名称,当作默认实现类的名称,是必须;
  • @Adaptive配置在实现类或者接口方法上;
    • 配置实现类,不用动态生成适配器类,且只能配置一个;
    • 配置接口方法上,作用于动态生产适配器时,动态实现方法;未配置不用实现接口方法,直接throw异常;
  • @Activate类注解,用于分组和key进行过滤;获取Filter集合;

三、ExtensionLoader源码分析

  • SPI是怎么加载接口实现类的class?
  • why设计适配器类?
  • 是怎么实现DI呢?
  • 用什么方式按分组和key获取相应的实现类集合?

从几个问题ExtensionLoader源码分析;

1.loadExtensionClasses方法加载接口实现类

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  // synchronized in getExtensionClasses
    private Map<String, Class<?>> loadExtensionClasses() {
        final SPI defaultAnnotation = type.getAnnotation(SPI.class);
        //设置cachedDefaultName 默认名称,就是注解SPI的value值
        if (defaultAnnotation != null) {
            String value = defaultAnnotation.value();
            if (value != null && (value = value.trim()).length() > 0) {
                String[] names = NAME_SEPARATOR.split(value);
                //不能配置多个name;
                if (names.length > 1) {
                    ...
                }
                if (names.length == 1) cachedDefaultName = names[0];
            }
        }
        //从指定文件加载相应值
        Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
        // 从META-INF/dubbo/internal/,META-INF/dubbo/和META-INF/services 三个文件中获取type接口的文件
        loadFile(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
        loadFile(extensionClasses, DUBBO_DIRECTORY);
        loadFile(extensionClasses, SERVICES_DIRECTORY);
        return extensionClasses;
    }

a.loadFile 从文件中加载内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
    private void loadFile(Map<String, Class<?>> extensionClasses, String dir) {
        String fileName = dir + type.getName();
        try {
            Enumeration<java.net.URL> urls;
            //获取类加载类,根据类加载获取文件URL
            ClassLoader classLoader = findClassLoader();//ExtensionLoader.class.getClassLoader
            if (classLoader != null) {
                urls = classLoader.getResources(fileName);
            } else {
                urls = ClassLoader.getSystemResources(fileName);
            }
            if (urls != null) {
                while (urls.hasMoreElements()) {
                    java.net.URL url = urls.nextElement();
                    try {
                        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
                        try {
                            String line = null;
                            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) {
                                    // 根据key=value 解析内容,valeu相应实现类
                                    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) {
                                            Class<?> clazz = Class.forName(line, true, classLoader);
                                            //判断type和clazz是否相同或是另一个类的子类或接口。
                                            if (!type.isAssignableFrom(clazz)) {
                                                ...
                                            }
                                            //类配置@Adaptive注解,切只有一个,赋值变量cachedAdaptiveClass
                                            if (clazz.isAnnotationPresent(Adaptive.class)) {
                                                if (cachedAdaptiveClass == null) {
                                                    cachedAdaptiveClass = clazz;
                                                } else if (!cachedAdaptiveClass.equals(clazz)) {
                                                    ...包含多个throw异常
                                                }
                                            } else {
                                                try {
                                                    //包装类,构造方法中个type属性
                                                    clazz.getConstructor(type);
                                                    //缓存包装类
                                                    Set<Class<?>> wrappers = cachedWrapperClasses;
                                                    if (wrappers == null) {
                                                        cachedWrapperClasses = new ConcurrentHashSet<Class<?>>();
                                                        wrappers = cachedWrapperClasses;
                                                    }
                                                    wrappers.add(clazz);
                                                } catch (NoSuchMethodException e) {
                                                    clazz.getConstructor();
                                                     //在文件中key为空,从注解Extension获取name,现在已用弃用
                                                    if (name == null || name.length() == 0) {
                                                        ...
                                                    }
                                                     //根据','配置多个name
                                                    String[] names = NAME_SEPARATOR.split(name)
                                                    if (names != null && names.length > 0) {
                                                        Activate activate = clazz.getAnnotation(Activate.class);
                                                        if (activate != null) {
                                                            //缓存注解Activate
                                                            cachedActivates.put(names[0], activate);
                                                        }
                                                        for (String n : names) {
                                                            if (!cachedNames.containsKey(clazz)) {
                                                                //缓存名字
                                                                cachedNames.put(clazz, n);
                                                            }
                                                           //扩展类加载到extensionClasses集合中
                                                            Class<?> c = extensionClasses.get(n);
                                                            if (c == null) {
                                                                extensionClasses.put(n, clazz);
                                                            } else if (c != clazz) {
                                                                ...相同的名字thow异常
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    } catch (Throwable t) {
                                        ...把异常信息添加到缓存中
                                    }
                                }
                            } 
                        } finally {
                            reader.close();
                        }
                    }...
                } 
            }
        }...
    }

loadFile逻辑缓存步骤:

  • (1).查找Class类上是否@Adaptive注解,查找Class类中,只有一个@Adaptive注解,有多个throw异常;缓存cachedAdaptiveClass; 假如成立,不执行2,3,4步骤;

  • (2).查找Class类中构造方法是否该类型Class,缓存到Set集合cachedWrapperClasses中;假如成立,不执行3,4步骤;

  • (3).查找Class类上是否@Activate注解,把Activate对象缓存到Map集合cachedActivates;

  • (4).缓存Map集合cachedNames中(class—>name);

b.getExtensionClasses 获取实现type接口的实现类集合,且只处理loadExtensionClasses一次,加载结果缓存;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    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;
    } 

2.看看获取适配器类的流程;从getAdaptiveExtension方法入口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  @SuppressWarnings("unchecked")
    public T getAdaptiveExtension() {
        //cachedAdaptiveInstance是Holder对象,且只能创建一个
        Object instance = cachedAdaptiveInstance.get();
        //cachedAdaptiveInstance缓存获取
        if (instance == null) {
            if (createAdaptiveInstanceError == null) {
                synchronized (cachedAdaptiveInstance) {
                    instance = cachedAdaptiveInstance.get();
                    if (instance == null) {  //双重验证
                        try {
                            //调用创建适配器类
                            instance = createAdaptiveExtension();
                            cachedAdaptiveInstance.set(instance);//缓存cachedAdaptiveInstance
                        } catch (Throwable t) {
                            createAdaptiveInstanceError = t;
                            ...
                        }
                    }
                }
            }...
        }
        return (T) instance;
    }

a.createAdaptiveExtension 创建实例对象

实现DI注入

1
2
3
4
5
6
    @SuppressWarnings("unchecked")
    private T createAdaptiveExtension() {
        try {
            return injectExtension((T) getAdaptiveExtensionClass().newInstance());
        }...
    }

b.getAdaptiveExtensionClass

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    private Class<?> getAdaptiveExtensionClass() {
        //处理加载实现类
        getExtensionClasses();
        //cachedAdaptiveClass 就是注解@Adaptive的Class
        if (cachedAdaptiveClass != null) {
            return cachedAdaptiveClass;
        }
        //动态生成适配器类
        return cachedAdaptiveClass = createAdaptiveExtensionClass();//缓存到cachedAdaptiveClass
    }

3.如何动态生成适配器类,createAdaptiveExtensionClass入口

1
2
3
4
5
6
7
8
9
    private Class<?> createAdaptiveExtensionClass() {
        //生成动态代码
        String code = createAdaptiveExtensionClassCode();
        ClassLoader classLoader = findClassLoader();
        //获取对象是AdaptiveCompiler适配类,默认获取@SPI注解的名称javassist,
        com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
        //最终交给JavassistCompiler编译成class,底层用javassist
        return compiler.compile(code, classLoader);
    }

a.createAdaptiveExtensionClassCode生成动态代码;

这个方法代码量大,不美观;按几个步骤去分析;

  • 按type+$Adaptive组装成类名
  • 遍历实现接口方法,
    • 是否有@Adaptive注解,否则:throw的UnsupportedOperationException
    • 从方法参数中查找到URL的位置,URL作用用于获取适配器类中调用相应实现类名称
    • URL用什么方式获取name呢?
      • @Adaptive注解配置value,value是数组,未配置,就获取type名称,有大写字母变成小写字母前面加’.';
      • 获取分三个种;
        • value包含protocol,获取方式url.getProtocol();
        • 参数包含Invocation类型,获取方式url.getMethodParameter(methodName,
        • 不满足上面情况,获取方式url.getParameter
      • value配置多个时,从大到小遍历;为空情况往后获取;则参数为Invocation情况,只取第一个情况;
    • extension = ExtensionLoader.getExtensionLoader(type.class).getExtension(extName)
    • extension拼接成调用相应方法

4.DI注入实现,injectExtension方法入口

  • 注入set方法
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    private T injectExtension(T instance) {
        try {
            if (objectFactory != null) {
                for (Method method : instance.getClass().getMethods()) {
                    //setter
                    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) : "";
                            //创建对象
                            Object object = objectFactory.getExtension(pt, property);
                            if (object != null) {
                                method.invoke(instance, object);
                            }
                        }...
                    }
                }
            }
        }...
        return instance;
    }

a.objectFactory对象创建注入的对象;就是ExtensionFactory

  • 自己实现适配类AdaptiveExtensionFactory
  • SpiExtensionFactory和SpringExtensionFactory实现类,
  • 获取对象时,遍历上面类,获取对象;

5.getExtensionLoader 获取ExtensionLoader实例,每个接口类型对应一个ExtensionLoader;

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
  @SuppressWarnings("unchecked")
    public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            ...
        if (!type.isInterface()) {
            ...异常
        }
        if (!withExtensionAnnotation(type)) { //type.isAnnotationPresent(SPI.class) SPI注解
            ... 异常
        }
        //先从EXTENSION_LOADERS缓存中获取ExtensionLoader对象,没用创建ExtensionLoader,在EXTENSION_LOADERS放置缓存中
        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;
    }

6.属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ExtensionLoader<T> {

    private static final Logger logger = LoggerFactory.getLogger(ExtensionLoader.class);
    //dubbo的SPI扫描文件地址
    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/";

    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;
    //DI注入工厂类
    private final ExtensionFactory objectFactory;
    //缓存(class-name)
    private final ConcurrentMap<Class<?>, String> cachedNames = new ConcurrentHashMap<Class<?>, String>();
    //缓存(name-class)
    private final Holder<Map<String, Class<?>>> cachedClasses = new Holder<Map<String, Class<?>>>();
    //缓存注解@Activate
    private final Map<String, Activate> cachedActivates = new ConcurrentHashMap<String, Activate>();
    //缓存包装过实例对象
    private final ConcurrentMap<String, Holder<Object>> cachedInstances = new ConcurrentHashMap<String, Holder<Object>>();
    //缓存Adaptive实例对象
    private final Holder<Object> cachedAdaptiveInstance = new Holder<Object>();
    //缓存被@Adaptive注解的Class
    private final Class<?> cachedAdaptiveClass = null;
    //接口@SIP中value值
    private String cachedDefaultName;
    //创建适配类异常信息
    private volatile Throwable createAdaptiveInstanceError;
    //缓存包装类
    private Set<Class<?>> cachedWrapperClasses;
    //操作loadFile方法是,异常保存
    private final Map<String, IllegalStateException> exceptions = new ConcurrentHashMap<String, IllegalStateException>();
}

7.构造

1
2
3
4
5
6
     //私有构造方法
    private ExtensionLoader(Class<?> type) {
        this.type = type;
        //当type是ExtensionFactory,objectFactory为null;
        objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension());
    }

8.getExtension 按名称获取对象

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    @SuppressWarnings("unchecked")
    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;
    }

a.createExtension 创建扩展类对象

  • 从cachedClasses缓存获取按name获取相应实现class
  • DI注入操作
  • 包装类操作
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   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, (T) clazz.newInstance());
                instance = (T) EXTENSION_INSTANCES.get(clazz);
            }
            injectExtension(instance);//setter注入
            Set<Class<?>> wrapperClasses = cachedWrapperClasses;//包装类集合缓存
            //层层包装这个类
            if (wrapperClasses != null && wrapperClasses.size() > 0) {
                for (Class<?> wrapperClass : wrapperClasses) {
                    instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
                }
            }
            return instance;
        }...
    }

9.getActivateExtension 按名称获取集合对象

  • values是否包含’-defualt’
    • 否,按组过滤,在按Url的参数是否包含values的key和value
    • 是不处理默认逻辑
  • 按values获取相应的name
  • 包含’-‘就是删除操作
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

  /**
   * Get activate extensions.
   *
   * @param url    url
   * @param values extension point names 过滤cachedActivates缓存中名称
   * @param group  group                 比配@Activate中group
   * @return extension list which are activated
   * @see com.alibaba.dubbo.common.extension.Activate
   */
  public List<T> getActivateExtension(URL url, String[] values, String group) {
      List<T> exts = new ArrayList<T>();
      List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
      if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {//比较- defualt
          getExtensionClasses();
          for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
              String name = entry.getKey();
              Activate activate = entry.getValue();
              if (isMatchGroup(group, activate.group())) {//比配@Activate中group属性
                  T ext = getExtension(name);
                  if (!names.contains(name)  
                          && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
                          && isActive(activate, url)) {//url参数集合中是否包含@Activate注解中value数组至少一个值
                      exts.add(ext);
                  }
              }
          }
          Collections.sort(exts, ActivateComparator.COMPARATOR);   //按@Activate注解进行排序
      }
      List<T> usrs = new ArrayList<T>();
      for (int i = 0; i < names.size(); i++) {
          String name = names.get(i);
          if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
                  && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
              if (Constants.DEFAULT_KEY.equals(name)) {
                  if (usrs.size() > 0) {
                      exts.addAll(0, usrs);
                      usrs.clear();
                  }
              } else {
                  T ext = getExtension(name);
                  usrs.add(ext);
              }
          }
      }
      if (usrs.size() > 0) {
          exts.addAll(usrs);
      }
      return exts;
  }

(1).isMatchGroup 比配@Activate中group属性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
    private boolean isMatchGroup(String group, String[] groups) {
        if (group == null || group.length() == 0) {
            return true;
        }
        if (groups != null && groups.length > 0) {
            for (String g : groups) {
                if (group.equals(g)) {
                    return true;
                }
            }
        }
        return false;
    }

(2).isActive url参数集合中是否包含@Activate注解中value数组至少一个值

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    private boolean isActive(Activate activate, URL url) {
        String[] keys = activate.value();
        if (keys == null || keys.length == 0) {
            return true;
        }
        for (String key : keys) {
            for (Map.Entry<String, String> entry : url.getParameters().entrySet()) {
                String k = entry.getKey();
                String v = entry.getValue();
                if ((k.equals(key) || k.endsWith("." + key))
                        && ConfigUtils.isNotEmpty(v)) {
                    return true;
                }
            }
        }
        return false;
    }

10.总结

  • 运用适配类动态获取相应的实现类;更好扩展;
  • 扩展DI注入以及注入可以集成spring的容器的bean;以及包装类实现;