当前位置: 移动技术网 > IT编程>开发语言>Java > Dubbo源码分析之 SPI(一)

Dubbo源码分析之 SPI(一)

2019年12月16日  | 移动技术网IT编程  | 我要评论

一、概述

    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功能等知识点,我们都暂且绕过了,后面我们会在下一篇文章中逐一介绍。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网