当前位置: 移动技术网 > IT编程>开发语言>Java > dubbo源码(章节二) -- dubbo的Ioc

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

2018年10月24日  | 移动技术网IT编程  | 我要评论

安族网,途美折扣,悠然自德 谦手十年

上一篇主要分析了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来获取扩展实现类,只有第三种实现类能通过这种方式被获取,但是如果该接口有包装类存在,那么此方法获取的永远是被包装的类。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网