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