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

dubbo源码(章节二) -- dubbo的Ioc

程序员文章站 2022-08-09 18:54:18
上一篇主要分析了extensionLoader的获取,以及获取extension的第一种方式,即通过装饰类或者动态代理的方式,今天我们首先从获取extension的第二种方式说起。 下面讨论getExtension(String name) 先从这个方法的代码跟踪开始, 这里省略了比较多的内容,有了 ......

上一篇主要分析了extensionloader的获取,以及获取extension的第一种方式,即通过装饰类或者动态代理的方式,今天我们首先从获取extension的第二种方式说起。

/**
 * find the extension with the given name.
 */
getextension(string name)

 

下面讨论getextension(string name)

先从这个方法的代码跟踪开始,

1 public t getextension(string name) {
2   holder<object> holder = cachedinstances.get(name); 3   ......
4   object instance = holder.get(); 5   ......
6   instance = createextension(name);
7   return (t) instance; 8 }

这里省略了比较多的内容,有了前面一篇跟踪代码的经验,缓存的具体使用就不再贴出来了,我们只看主要逻辑即可,跟进去看createextension(name),

 1 private t createextension(string name) {
 2   class<?> clazz = getextensionclasses().get(name); 
3   t instance = clazz.newinstance(); 4
5   injectextension(instance); 6   set<class<?>> wrapperclasses = cachedwrapperclasses;
7   for (class<?> wrapperclass : wrapperclasses) { 8     instance =
9     injectextension((t) wrapperclass.getconstructor(type).newinstance(instance)); 10   }
11   return instance;
12 }

这里先是通过getextensionclasses().get(name)拿到一个class对象,getextensionclasses()方法上一篇说过了,它最终所赋值的是type扩展的所有实现中,既没有@adaptive注解,也不包含type类型的构造器(这一类扩展实现我们称之为包装类)的那些实现类,并且被缓存在cachedclasses map中,map的key即为实现类在dubbo spi配置文件中的类名,这里提一个细节,如果在cachedclasses中没有拿到key为name对应的value,这里就会抛出异常,那也就是说,getextension(name)这种方式,只能用来获取既非@adaptive注解,又非包装类的那些类的实现,因为只有这样的类才会被缓存在cachedclasses中。这里拿到这个类并实例化一个instance,然后调用了方法injectextension(instance),这个方法看名字就知道了,inject既就是注射的意思,这里就将实现dubbo的依赖注入,现在来看这个方法的实现,

 1 private t injectextension(t instance) { 
2   for (method method : instance.getclass().getmethods()) { 3     if (method.getname().startswith("set") 4         && method.getparametertypes().length == 1 5         && modifier.ispublic(method.getmodifiers())) { 6       class<?> pt = method.getparametertypes()[0]; 7
8       string property = method.getname().length() > 3 ?
9         method.getname().substring(3, 4).tolowercase() +
10         method.getname().substring(4) : ""; 11       object object = objectfactory.getextension(pt, property);
12       method.invoke(instance, object);
13     }
14   }
15   return instance; 16 }

把这个class的所有方法都提取出来,然后做了一系列的判断,这就是要通过setter方法为对象注入属性了。被注入的object是通过objectfactory.getextension(...)得到的,回忆一下上一篇说过,每个extension对象都会有一个objectfactory,objectfactory就是一个adaptiveextensionfactory,它的作用是:为dubbo的ioc提供所有对象。所以这里我们看到,setter方法要注入的属性值,是通过这个扩展的objectfactory拿到的,我们跟进去看一下它的实现,

1 public <t> t getextension(class<t> type, string name) {
2   for (extensionfactory factory : factories) {
3     t extension = factory.getextension(type, name);
4     return extension;
5   }
6 }

这里会遍历factories,每个元素都是extensionfactory的一个非adaptive实现,我们在上一篇已经看到了,extensionfactory的非adpative实现,最终被放入factories中的,是spiextensionfactory,不过实际上在另外的包里还有一个extensionfactory的非adaptive实现类:springextensionfactory,这里我们不妨把spiextensionfactory和springextensionfactory同时拿来分析,可以看到只要这里getextension(type,name)返回非空,就直接返回所获取的这个值,我们依次看下这两个实现分别是怎么做的,先看spiextensionfactory:

1 public <t> t getextension(class<t> type, string name) {
2   if (type.isinterface() && type.isannotationpresent(spi.class)) {
3     extensionloader<t> loader = extensionloader.getextensionloader(type);
4     if (!loader.getsupportedextensions().isempty()) {
5       return loader.getadaptiveextension();
6     }
7   }
8   return null;
9 }

这里判断type是不是@spi标注的注解,如果是,说明这个type是一个dubbo spi扩展,那么就返回它的一个adaptiveextension,它为什么叫spiextensionfactory呢?就是获取spi扩展的一个factory,获取spi扩展的adaptive实现的做法在上一篇里已经讨论过了。也就是说,如果一个扩展实现类中需要注入另一个spi扩展的实现,那就是通过它的objectfactory里的spiextensionfactory来获取需要注入的这个扩展的adaptive实现。

那么如果要注入的对象不是dubbo spi扩展呢,这里不会进入if,就会返回null,我们接下来看springextensionfactory,

 1 public <t> t getextension(class<t> type, string name) {
 2   for (applicationcontext context : contexts) {
 3     if (context.containsbean(name)) {
 4       object bean = context.getbean(name); 
5       return (t) bean;
6     } 7   } 8 9   for (applicationcontext context : contexts) {
10     return context.getbean(type);
11   } 12   return null; 13 }

springextensionfactory就很简单了,直接从spring的applicationcontext中尝试获取bean,先尝试通过name获取,如果by name失败,再尝试通过by type来获取。也就是说,如果一个扩展实现类中需要注入一个普通对象(非spi注解的dubbo扩展),那就通过它的objectfactory里的springextensionfactory来获取这个要被注入的类对象,当然前提是spring容器中已经注入了这个对象。

ok,通过objectfactory提供的对象,我们完成了extension属性的注入,不过createextension方法所做的并不止这些,我们回到injectextension的调用处,即createextension方法的第五行,接着往下看,代码第六行获取了缓存cachedwrapperclasses,上一篇讲了,这个变量缓存了所有非@adaptive注解同时包含了带有本扩展类型的构造器方法的那些扩展实现类。实际上从这个缓存的名字里就能看出来,wrapper意指包装,就是说这里所包含的类都是包装类。为了解释这行代码,这里必须举一个例子:我们看接口protocol,

@spi("dubbo")
public interface protocol{
  ......  
}

这是一个@spi注解的dubbo扩展接口,考虑它的三个实现,mockprotocol,protocolfilterwrapper,protocollistenerwrapper,

final public class mockprotocol implements protocol {
  @override
  public int getdefaultport() {
    return 0;
  }
  ......
}
public class protocolfilterwrapper implements protocol {
  private final protocol protocol;

  public protocolfilterwrapper(protocol protocol) {
    if (protocol == null) {
      throw new illegalargumentexception(......);
    }
    this.protocol = protocol;
  }
  @override
  public int getdefaultport() {
    return protocol.getdefaultport();
  }
  ...... }
public class protocollistenerwrapper implements protocol {
  private final protocol protocol;

  public protocollistenerwrapper(protocol protocol) {
    if (protocol == null) {
      throw new illegalargumentexception(......);
    }
    this.protocol = protocol;
  }
  @override
  public int getdefaultport() {
    return protocol.getdefaultport();
  }
  ...... }

可以看到,这三个类都没有被@adaptive注解,其中protocolfilterwrapper,protocollistenerwrapper都有一个私有属性protocol,同时有一个protocol类型作为入参的构造器,所以在类加载之后,这两个类都会被放入cachedwrapperclasses缓存中,而mockprotocol则既不被@adaptive注解,也不包含protocol作为入参的构造器,它在类加载之后会被放入cachedclasses中,所以它是可以被通过第二种获取扩展对象的方式获取的:

extensionloader.getextensionloader(protocol.class).getextension("mock");

其中"mock"为dubbo spi配置文件中该类的name。

ok,当mockprotocol被注入了属性之后,代码获取了cachedwrapperclasses的值,然后依次取出其中缓存的类,初始化它们,并将当前protocol作为参数传入构造器,同时将返回的protocol赋值给当前instance。所以,如果cachedwrapperclasses中的顺序是:protocolfilterwrapper,protocollistenerwrapper,那么执行完上述代码之后,在createextension方法的最后一行,我们最终获取到的instance将不再是mockprotocol的实例,而是protocollistenerwrapper的实例,它里面拥有一个protocol属性,此protocol将会是protocolfilterwrapper的实例,而它里面还是拥有一个protocol属性,这个protocol才是我们一开始就拿到的mockprotocol的实例。如下所示;

instance(protocollistenerwrapper@947)
    -->protocol(protocolfilterwrapper@950)
        -->protocol(mockprotocol@874)

看到这,想必大家就明白了为什么这一类的实现类,会被称为包装类了吧。如果调用instance的方法,例如代码中贴出来的getdefaultport(),就将从包装的最外层开始向内调用。

ok,这一篇我们分析了dubbo获取扩展实现的第二种方式,同时分析了dubbo ioc的原理,总结下dubbo的spi:

  • 要获取dubbo spi接口的实现,就要先获取对应的extensionloader,而通过loader获取实现的方式有两种。
  • getextensionclasses()方法会加载配置文件中配置的该接口的所有实现,并赋值给相应的缓存:
    • 接口的所有实现中,要么存在唯一的一个类被@adaptive注解,要么就动态生成一个adaptive代理类。这个类被缓存在cachedadaptiveclass中,我们称之为第一种实现类。
    • 接口的所有实现中,如果存在一些实现,没有被@adaptive注解,但是包含一个以该接口类型为参数的构造器,称这种类为第二种实现类或包装类,它们被缓存在cachedwrapperclasses中。
    • 剩下的实现类,既不被@adaptive注解,也不包含特殊的构造器,我们称之为第三种实现类,它们被缓存在cachedclasses中。
  • getadaptiveextension()方法将获得扩展接口的装饰模式的实现类,这个类有且只有一个。
  • getextension(name)方法根据配置文件中的类的name来获取扩展实现类,只有第三种实现类能通过这种方式被获取,但是如果该接口有包装类存在,那么此方法获取的永远是被包装的类。