欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Dubbo源码分析之 SPI(一)

程序员文章站 2023-02-08 13:12:13
一、概述 dubbo SPI 在dubbo的作用是基础性的,要想分析研究dubbo的实现原理、dubbo源码,都绕不过 dubbo SPI,掌握dubbo SPI 是征服dubbo的必经之路。 本篇文章会详细介绍dubbo SPI相关的内容,通过源码分析,目标是让读者能通过本篇文章,彻底征服dubb ......

一、概述

    dubbo spi 在dubbo的作用是基础性的,要想分析研究dubbo的实现原理、dubbo源码,都绕不过 dubbo spi,掌握dubbo spi 是征服dubbo的必经之路。

    本篇文章会详细介绍dubbo spi相关的内容,通过源码分析,目标是让读者能通过本篇文章,彻底征服dubbo spi。

    文章的组织方式是先介绍spi 的概念,通过java spi 让大家了解spi 是什么,怎么用,有一个初步的概念,dubbo的spi是扩展了java spi的内容,自己实现了一个spi。

二、spi概念介绍

    spi全称 service provider interface,是一种服务发现机制。我们编程实现一个功能时,经常先抽象一个interface,内部再定一些方法。具体的实现交给 implments了此接口的类,实现了功能和实现类的分离。

    我们设想一下,如果一个描述汽车功能的interface  car,存在多个实现类bmw、benz、volvo,某个场景调用car的行驶方法,程序就需要确认到底是需要bmw、benz、volvo中的那个类对象。如果硬编码到程序中,固然可以,但是出现了接口和实现类的耦合,缺点也显而易见。

    有办法做到在调用代码中不涉及bmw、benz、volvo字符,也随意的指定实现类么?当然,spi就是解决这个问题。

    spi的实现方式是,在指定的路径下增加一个文本文件,文件的名称是interface的全限定名(包名+接口名),文件内容每行是一个此接口的实现类的全限定名,多个实现类就会有多行。接口进行调用时,根据接口全限定名,先读取文本文件,解析出具体的实现类,通过反射进行实例化,再调用之。如果增加新的实现类,不需要修改调用代码,只需要在文本文件中增加一行实现类的全限定名即可,删除实现类同理。

三、java spi 介绍

    我们先看看java的spi怎么实现的,通过一个demo,进行了解。

    先定一个小车的接口,有一个方法 gobeijing():

1 package cn.hui.spi
2 //car 接口
3 public interface car {
4 
5     // 开车去北京
6     void gobeijing();
7 
8 }

    我们建三个实现类:

    

 1 package cn.hui.spi.impl;
 2 public class bmw implements car{
 3     @override
 4     public void gobeijing() {
 5         // todo auto-generated method stub
 6         system.out.println("开着宝马去北京......");
 7     }
 8 }
 9 
10 package cn.hui.spi.impl;
11 public class benz implements car{
12     @override
13     public void gobeijing() {
14         // todo auto-generated method stub
15         system.out.println("开着奔驰去北京........");
16     }
17 }
18 
19 package cn.hui.spi.impl;
20 public class volvo implements car {
21     @override
22     public void gobeijing() {
23         // todo auto-generated method stub
24         system.out.println("开着沃尔沃去北京......");
25     }
26 }

    我们在 "meta-inf/services" 文件夹下新建一个文件,名称为“cn.hui.spi.car",文件内容:

    

1 cn.hui.spi.impl.bmw
2 cn.hui.spi.impl.benz
3 cn.hui.spi.impl.volvo

     方法调用的代码如下:

    

 1 import java.util.iterator;
 2 import java.util.serviceloader;
 3 public class app {
 4     
 5     public static void main(string[] args) {
 6         serviceloader<car> serviceloader = serviceloader.load(car.class);
 7         iterator<car> iterator = serviceloader.iterator();
 8         while(iterator.hasnext()) {
 9             car car = iterator.next();
10             car.gobeijing();
11         }
12     }
13 }

    打印结果:

1 开着宝马去北京......
2 开着奔驰去北京........
3 开着沃尔沃去北京......

    这个就是java spi简单实现方式。

三、dubbo spi介绍

    dubbo 在java spi的基础上进行了功能扩展,我们再看上面的java spi示例,可以发现很明显的问题,对文本文件的加载后,实例化对象是一次性全部进行实例化,得到一个实现类的对象集合,调用的时候循环执行。不能唯一指定一个实现类进行唯一调用。dubbo通过在文本文件中指定每个实现类的key,唯一标识出每个实现类,调用的时候可以指定唯一的一个实现类。同样实例化也不需要一次性全部实例化了,只需要实例化需要调用的类即可。

     同时dubbo还实现了ioc和aop的功能,接口的实现类之间可以进行相互的注入,了解spring的同学,应该很清楚ioc和aop的逻辑,下面我们现在熟悉下dubbo spi的相关概念,之后在通过一个简单的样例,了解dubbo spi 的使用。

四、dubbo spi关键点

    dubbo spi的功能主要是通过extensionloader类实现,dubbo启动时,默认扫描三个目录:meta-inf/services/、meta-inf/dubbo/、meta-inf/internal/,在这三个目录下的文本文件都会加载解析,文本文件的内容:key=实现类的全限定名。

    dubbo把接口定义为 扩展点,实现类定义为 扩展点实现,所有的扩展点(接口)需要进行@spi注解,更多的功能和注解我们逐步介绍。

    dubbo在启动的时候扫描文本文件,对文件内容进行解析,但是不会全部进行实例化,只有在调用到具体的扩展点实现时,才会进行特定扩展点的实例化。

    同时dubbo spi提供自适应扩展、默认扩展、自动激活扩展等功能,我们后面介绍。

五、dubbo spi示例

    我们把上面car接口的例子,改造成基于dubbo spi的实现。进行配置的文本文件内容。

    在扩展点实现类前都加上key,改为:

1 bmw=cn.hui.spi.impl.bmw
2 benz=cn.hui.spi.impl.benz
3 volvo=cn.hui.spi.impl.volvo

 car接口改造为:

1 @spi
2 public interface car {
3     // 开车去北京
4     void gobeijing();
5 }

扩展点,暂时不做修改,我们看看调用方法:

 1 public class app {
 2     public static void main(string[] args) {
 3         car car = extensionloader.getextensionloader(car.class).getextension("bmw");
 4         car.gobeijing();
 5         car = extensionloader.getextensionloader(car.class).getextension("benz");
 6         car.gobeijing();
 7         car = extensionloader.getextensionloader(car.class).getextension("volvo");
 8         car.gobeijing();
 9     }
10 }

此时,控制台会出现:

1 开着宝马去北京......
2 开着奔驰去北京........
3 开着沃尔沃去北京......

这个就是简单dubbo使用,复杂的功能我们放到源码分析的时候进行。

六、dubbo spi 源码分析

    dubbo spi的功能主要几种在extensionloader类中实现,分析源码也就主要分析此类,我们通过extensionloader对外提供的方法作为入口进行源码分析。

    需要注意:一个type接口对应一个extensionloader 实例。

    上面的示例中,我们通过 getextensionloader(..)方法,获得extensionloader实例,extensionloader类的构造方法是私有的,只能通过此方法获取实例。

    我们先看看此方法:

 1 @suppresswarnings("unchecked")
 2 public static <t> extensionloader<t> getextensionloader(class<t> type) {
 3     if (type == null) {
 4         throw new illegalargumentexception("extension type == null");
 5     }
 6     // 必须是接口
 7     if (!type.isinterface()) {
 8         throw new illegalargumentexception("extension type(" + type + ") is not interface!");
 9     }
10     // 必须被@spi注解
11     if (!withextensionannotation(type)) {
12         throw new illegalargumentexception("extension type(" + type + ") is not extension, because without @" + spi.class.getsimplename() + " annotation!");
13     }
14     // extension_loaders 为成员变量,是 type---> extensionloader 实例的缓存
15     extensionloader<t> loader = (extensionloader<t>) extension_loaders.get(type);
16     if (loader == null) {
17         // putifabsent put不覆盖
18         extension_loaders.putifabsent(type, new extensionloader<t>(type));
19         loader = (extensionloader<t>) extension_loaders.get(type);
20     }
21     return loader;
22 }

我们看到该方法主要是先对type进行校验,再根据type为key,从缓存extension_loaders中获取extensionloader实例,如果缓存没有,则新建一个extensionloader实例,并放入缓存。
注意,我们说过一个type对应一个extensionloader实例,为什么还需要缓存呢,我们再看看 extension_loaders的定义:

// 扩展点接口和对应extensionloader实例的缓存
private static final concurrentmap<class<?>, extensionloader<?>> extension_loaders = new concurrenthashmap<class<?>, extensionloader<?>>();

没错,extension_loaders 是一个static、final修饰的类静态变量。

我们接着看上面,看一下extensionloader的构造方法:

 1 private extensionloader(class<?> type) {
 2     this.type = type;
 3     // type 为extensionfactory时,objectfactory为空
 4     if (type == extensionfactory.class) {
 5         objectfactory = null;
 6     } else {
 7         // type为普通接口时,objectfactory为adaptiveextensionfactory,负责dubbo spi 的ioc 功能
 8         objectfactory = extensionloader.getextensionloader(extensionfactory.class).getadaptiveextension();
 9     }
10 //        objectfactory = (type == extensionfactory.class ? null
11 //                : extensionloader.getextensionloader(extensionfactory.class).getadaptiveextension());
12 }

构造方法私有,不能直接在外部new出实例。

方法内部,参数type赋值给成员变量type,还会进行extensionfactory类判断,extensionfactory是实现ioc功能的,我们此处暂时绕过,后面进行介绍。
我们总结一下getextensionloader(..)方法,绕开extensionfactory,就是new 了一个extensionloader对象实例,为成员变量type赋值为扩展点type,对象实例放入extension_loaders 缓存中。
现在我们有了extensionloader实例对象,我们再看看获取type实例的方法:getextension(..):

 1 @suppresswarnings("unchecked")
 2 public t getextension(string name) {
 3     if (name == null || name.length() == 0)
 4         throw new illegalargumentexception("extension name == null");
 5     if ("true".equals(name)) {
 6         // 获取默认的扩展实现类
 7         return getdefaultextension();
 8     }
 9     // holder仅用于持有目标对象,没有其他逻辑
10     holder<object> holder = cachedinstances.get(name);
11     if (holder == null) {
12         cachedinstances.putifabsent(name, new holder<object>());
13         holder = cachedinstances.get(name);
14     }
15     object instance = holder.get();
16     if (instance == null) {
17         synchronized (holder) {
18             instance = holder.get();
19             if (instance == null) {
20                 // 创建扩展实例,并设置到holder中
21                 instance = createextension(name);
22                 holder.set(instance);
23             }
24         }
25     }
26     return (t) instance;
27 }

    方法的入参name为提供配置的文本文件中的key,还记得我们的文本文件中的内容吧,其中一行:bmw=cn.hui.spi.impl.bmw,此处的name 就是 bmw。 如果name为true,返回getdefaultextension(),这个方法我们暂时绕过。

 我们看到13行,根据name从cachedinstances中获取holder对象,很明显 cachedinstances就是一个存放对象的缓存,缓存中没有new一个新的实例,至于holder,我们看下这个类:

 1 // 持有目标对象
 2 public class holder<t> {
 3 
 4     private volatile t value;
 5 
 6     public void set(t value) {
 7         this.value = value;
 8     }
 9 
10     public t get() {
11         return value;
12     }
13 
14 }

只是存放对象,没有任何逻辑。

我们接着看到extensionloader类的代码,在拿到holder实例后,我们要从hodler中获取扩展点的实例:

 1 object instance = holder.get();
 2 if (instance == null) {
 3     synchronized (holder) {
 4         instance = holder.get();
 5         if (instance == null) {
 6             // 创建扩展实例,并设置到holder中
 7             instance = createextension(name);
 8             holder.set(instance);
 9         }
10     }
11 }

如果holder中没有扩展点的实例,通过双检锁,通过调用 createextension方法 返回扩展点实例。并放入holder对象中。

到此,我们发现new扩展点实例进到 createextension方法中。

我们接着分析此方法:

 1 // 创建扩展对象实例
 2 @suppresswarnings("unchecked")
 3 private t createextension(string name) {
 4     // 从配置文件中加载所有的扩展类,形成配置项名称到配置类clazz的映射关系
 5     class<?> clazz = getextensionclasses().get(name);
 6     if (clazz == null) {
 7         throw findexception(name);
 8     }
 9     try {
10         t instance = (t) extension_instances.get(clazz);
11         if (instance == null) {
12             // 通过反射创建实例
13             extension_instances.putifabsent(clazz, clazz.newinstance());
14             instance = (t) extension_instances.get(clazz);
15         }
16         // 向实例中注入依赖,ioc实现
17         injectextension(instance);
18         // 包装处理
19         // cachedwrapperclasses 加载@spi配置时赋值,此处进行实例化
20         set<class<?>> wrapperclasses = cachedwrapperclasses;
21         if (wrapperclasses != null && !wrapperclasses.isempty()) {
22             // 循环创建wrapper实例
23             for (class<?> wrapperclass : wrapperclasses) {
24                 // 将当前instance作为参数创建wrapper实例,然后向wrapper实例中注入属性值,
25                 // 并将wrapper实例赋值给instance
26                 instance = injectextension((t) wrapperclass.getconstructor(type).newinstance(instance));
27             }
28         }
29         return instance;
30     } catch (throwable t) {
31         throw new illegalstateexception("extension instance(name: " + name + ", class: " + type + ")  could not be instantiated: " + t.getmessage(), t);
32     }
33 }

    我们看到方法开始就通过 class<?> clazz = getextensionclasses().get(name); 获取class对象,可以直观的看出通过name获得的这个clazz是在配置的文本文件中name对应的扩展点实现类的class对象,关于getextensionclasses方法,我们稍后分析,接着往下看:

1 t instance = (t) extension_instances.get(clazz);
2 if (instance == null) {
3     // 通过反射创建实例
4     extension_instances.putifabsent(clazz, clazz.newinstance());
5     instance = (t) extension_instances.get(clazz);
6 }

通过clazz对象,从extension_instances获取缓存的实例,如果获取不到,通过反射clazz.newinstance() new一个新的实例对象,并放入extension_instances中。

我们可以看到,扩展点的实现类 必须要有一个默认无参的构造函数。

接着往下看:

1  // 向实例中注入依赖,ioc实现
2 injectextension(instance);

此方法是实现ioc功能,我们暂且绕过。

接下来,我们看到:

 1  // 包装处理
 2  // cachedwrapperclasses 加载@spi配置时赋值,此处进行实例化
 3  set<class<?>> wrapperclasses = cachedwrapperclasses;
 4  if (wrapperclasses != null && !wrapperclasses.isempty()) {
 5      // 循环创建wrapper实例
 6      for (class<?> wrapperclass : wrapperclasses) {
 7          // 将当前instance作为参数创建wrapper实例,然后向wrapper实例中注入属性值,
 8          // 并将wrapper实例赋值给instance
 9          instance = injectextension((t) wrapperclass.getconstructor(type).newinstance(instance));
10      }
11  }

此处是处理包装类的,我们也暂且绕过。下面就是直接返回扩展点的instance实例了 

1 return instance;

 现在我们还有一个方法没有分析,就是加载扩展点实现类的class对象的方法getextensionclasses()。我们现在来看这个方法:

 1 private map<string, class<?>> getextensionclasses() {
 2     map<string, class<?>> classes = cachedclasses.get();
 3     if (classes == null) {
 4         synchronized (cachedclasses) {
 5             classes = cachedclasses.get();
 6             if (classes == null) {
 7                 classes = loadextensionclasses();
 8                 cachedclasses.set(classes);
 9             }
10         }
11     }
12     return classes;
13 }

我们看到,这个方法返回的是一个map对象,可以确认的是,这个map存放的是扩展点的所有实现类的class,map的key就是配置的文本文件的name。如果缓存cachedclasses 中存在,即返回,如果没有,通过loadextensionclasses()加载,并设置到cachedclasses中。

我们接着看loadextensionclasses方法:

 1 private map<string, class<?>> loadextensionclasses() {
 2     // 获取注解 spi的接口
 3     // type为传入的扩展接口,必须有@spi注解
 4     final spi defaultannotation = type.getannotation(spi.class);
 5     // 获取默认扩展实现value,如果存在,赋值给cacheddefaultname
 6     if (defaultannotation != null) {
 7         string value = defaultannotation.value();
 8         if ((value = value.trim()).length() > 0) {
 9             // @spi value 只能是一个,不能为逗号分割的多个
10             // @spi value为默认的扩展实现
11             string[] names = name_separator.split(value);
12             if (names.length > 1) {
13                 throw new illegalstateexception("more than 1 default extension name on extension " + type.getname() + ": " + arrays.tostring(names));
14             }
15             if (names.length == 1)
16                 cacheddefaultname = names[0];
17         }
18     }
19     // 加载三个目录配置的扩展类
20     map<string, class<?>> extensionclasses = new hashmap<string, class<?>>();
21     // meta-inf/dubbo/internal
22     loaddirectory(extensionclasses, dubbo_internal_directory);
23     // meta-inf/dubbo
24     loaddirectory(extensionclasses, dubbo_directory);
25     // meta-inf/services/
26     loaddirectory(extensionclasses, services_directory);
27     return extensionclasses;
28 }

我们看到方法内部的逻辑,首先判断扩展点接口type是否用@spi注解,在前面的方法中,已经判断,如果没有@spi注解,抛出异常,此处type必定存在@spi注解。

根据注解获取到defaultannotation 对象,目的是拿到@spi中的value,且value值不能用逗号分隔,只能有一个,赋值给cacheddefaultname。

接着定一个了map对象extensionclasses,作为方法的返回值,我们知道,这个方法的返回值最后设置到了缓存cachedclasses中。我们看看这个extensionclasses是怎么赋值的。这个对象主要是”经历“了三个方法(其实是同一个方法loaddirectory,只是入参不同)。这三个方法的入参是extensionclasses 和一个目录参数,就是前面我们介绍的dubbo默认三个目录:

1 meta-inf/services/
2 meta-inf/dubbo/
3 meta-inf/dubbo/internal/

我们再具体看方法loaddirectory的内容:

 1 private void loaddirectory(map<string, class<?>> extensionclasses, string dir) {
 2     // 扩展配置文件完整文件路径+文件名
 3     string filename = dir + type.getname();
 4     try {
 5         enumeration<java.net.url> urls;
 6         // 获取类加载器
 7         classloader classloader = findclassloader();
 8         if (classloader != null) {
 9             urls = classloader.getresources(filename);
10         } else {
11             urls = classloader.getsystemresources(filename);
12         }
13         if (urls != null) {
14             while (urls.hasmoreelements()) {
15                 java.net.url resourceurl = urls.nextelement();
16                 // 加载
17                 loadresource(extensionclasses, classloader, resourceurl);
18             }
19         }
20     } catch (throwable t) {
21         logger.error("exception when load extension class(interface: " + type + ", description file: " + filename + ").", t);
22     }
23 }

首先组合目录参数和type名称,作为文件的真实路径名,通过加载器进行加载,之后调用loadresource方法,同时extensionclasses 传入该方法。

 1 private void loadresource(map<string, class<?>> extensionclasses, classloader classloader, java.net.url resourceurl) {
 2     try {
 3         bufferedreader reader = new bufferedreader(new inputstreamreader(resourceurl.openstream(), "utf-8"));
 4         try {
 5             string line;
 6             while ((line = reader.readline()) != null) {
 7                 // 字符#是注释开始标志,只取#前面的字符
 8                 final int ci = line.indexof('#');
 9                 if (ci >= 0)
10                     line = line.substring(0, ci);
11                 line = line.trim();
12                 if (line.length() > 0) {
13                     try {
14                         string name = null;
15                         int i = line.indexof('=');
16                         if (i > 0) {
17                             // 解析出 name 和 实现类
18                             name = line.substring(0, i).trim();
19                             line = line.substring(i + 1).trim();
20                         }
21                         if (line.length() > 0) {
22                             loadclass(extensionclasses, resourceurl, class.forname(line, true, classloader), name);
23                         }
24                     } catch (throwable t) {
25                         illegalstateexception e = new illegalstateexception("failed to load extension class(interface: " + type + ", class line: " + line + ") in " + resourceurl + ", cause: " + t.getmessage(), t);
26                         exceptions.put(line, e);
27                     }
28                 }
29             }
30         } finally {
31             reader.close();
32         }
33     } catch (throwable t) {
34         logger.error("exception when load extension class(interface: " + type + ", class file: " + resourceurl + ") in " + resourceurl, t);
35     }
36 }

 这个方法就简单多了,解析文件流,拿到配置文本文件中的key、value,同时通过class.forname(..)加载解析出来的扩展点实现类,传入方法loadclass,注意这个方法传入的参数还有存放key、class的map对象extensionclasses,以及配置文本文件中的key,我们再看这个方法:

 1 private void loadclass(map<string, class<?>> extensionclasses, java.net.url resourceurl, class<?> clazz, string name) throws nosuchmethodexception {
 2     // type是否为clazz的超类,clazz是否实现了type接口
 3     // 此处clazz 是扩展实现类的class
 4     if (!type.isassignablefrom(clazz)) {
 5         throw new illegalstateexception("error when load extension class(interface: " + type + ", class line: " + clazz.getname() + "), class " + clazz.getname() + "is not subtype of interface.");
 6     }
 7     // clazz是否注解了 adaptive 自适应扩展
 8     // 不允许多个类注解adaptive
 9     // 注解adaptive的实现类,赋值给cachedadaptiveclass
10     if (clazz.isannotationpresent(adaptive.class)) {
11         if (cachedadaptiveclass == null) {
12             cachedadaptiveclass = clazz;
13             // 不允许多个实现类都注解@adaptive
14         } else if (!cachedadaptiveclass.equals(clazz)) {
15             throw new illegalstateexception("more than 1 adaptive class found: " + cachedadaptiveclass.getclass().getname() + ", " + clazz.getclass().getname());
16         }
17         // 是否为包装类,判断扩展类是否提供了参数是扩展点的构造函数
18     } else if (iswrapperclass(clazz)) {
19         set<class<?>> wrappers = cachedwrapperclasses;
20         if (wrappers == null) {
21             cachedwrapperclasses = new concurrenthashset<class<?>>();
22             wrappers = cachedwrapperclasses;
23         }
24         wrappers.add(clazz);
25         // 普通扩展类
26     } else {
27         // 检测 clazz 是否有默认的构造方法,如果没有,则抛出异常
28         clazz.getconstructor();
29         // 此处name为 spi配置中的key
30         // @spi配置中key可以为空,此时key为扩展类的类名(getsimplename())小写
31         if (name == null || name.length() == 0) {
32             // 兼容旧版本
33             name = findannotationname(clazz);
34             if (name.length() == 0) {
35                 throw new illegalstateexception("no such extension name for the class " + clazz.getname() + " in the config " + resourceurl);
36             }
37         }
38         // 逗号分割
39         string[] names = name_separator.split(name);
40         if (names != null && names.length > 0) {
41             // 获取activate注解
42             activate activate = clazz.getannotation(activate.class);
43             if (activate != null) {
44                 cachedactivates.put(names[0], activate);
45             }
46             for (string n : names) {
47                 if (!cachednames.containskey(clazz)) {
48                     cachednames.put(clazz, n);
49                 }
50                 // name不能重复
51                 class<?> c = extensionclasses.get(n);
52                 if (c == null) {
53                     extensionclasses.put(n, clazz);
54                 } else if (c != clazz) {
55                     throw new illegalstateexception("duplicate extension " + type.getname() + " name " + n + " on " + c.getname() + " and " + clazz.getname());
56                 }
57             }
58         }
59     }
60 }

 方法参数clazz就是传过来的扩展点实现类的class对象,首先判断是否实现了扩展点type接口。接着判断是否注解了@adaptive以及是否为包装类iswrapperclass(clazz),这两个分支逻辑 我们暂且绕过,接下来会进行构造器检查,判断是否存在无参构造器,如果name为空,为了兼容老版本 会进行一次name赋值。

此处会再进行一次name的分隔,前门已经知道,name中不会存在逗号的,但经过上面兼容老版本的重新赋值,会再进行一次判断。@activate注解的判断,我们也暂且绕过。

循环解析过的name字符串,把加载的扩展点实现class对象和name存放到入参extensionclasses中。

至此,解析、加载配置文本文件的逻辑已经结束。最后的结果主要是有:把加载到的扩展点class和key存入到缓存对象extensionclasses中,同时设置cacheddefaultname为扩展点注解@spi中的value。

我们重新回到方法createextension中,现在我们已经拿到了特定name对应的扩展点实现类的class对象,如果对象为空,抛出异常。

接着,我们从缓存对象extension_instances中,通过class对象获取实例,如果实例为空,通过clazz.newinstance()创建,并放入extension_instances中。

createextension方法的后面的逻辑:

 1 // 向实例中注入依赖,ioc实现
 2 injectextension(instance);
 3 // 包装处理
 4 // cachedwrapperclasses 加载@spi配置时赋值,此处进行实例化
 5 set<class<?>> wrapperclasses = cachedwrapperclasses;
 6 if (wrapperclasses != null && !wrapperclasses.isempty()) {
 7     // 循环创建wrapper实例
 8     for (class<?> wrapperclass : wrapperclasses) {
 9         // 将当前instance作为参数创建wrapper实例,然后向wrapper实例中注入属性值,
10         // 并将wrapper实例赋值给instance
11         instance = injectextension((t) wrapperclass.getconstructor(type).newinstance(instance));
12     }
13 }

 是拿到扩展点的实例之后,后期的处理,包括对ioc的实现,包装类的处理等功能逻辑,这些知识点,我们稍后进行分析。

七、总结

    总结一下,本篇文章,我们分析了dubbo spi的主流程,从入门介绍、示例描述到源码分析,主流程基本介绍完了,中间涉及到的@adaptive、@activate注解,以及包装类、扩展点实现类的ioc功能等知识点,我们都暂且绕过了,后面我们会在下一篇文章中逐一介绍。