dubbo源码(章节二) -- dubbo的Ioc
2018-10-24 08:45:48来源:博客园 阅读 ()
上一篇主要分析了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来获取扩展实现类,只有第三种实现类能通过这种方式被获取,但是如果该接口有包装类存在,那么此方法获取的永远是被包装的类。
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:简单来看看JavaBean
下一篇:Java并发编程之原子操作解读
- 你说研究过Spring里面的源码,循环依赖你会么? 2020-06-09
- Dubbo+Zookeeper集群案例 2020-06-09
- 通俗理解spring源码(六)—— 默认标签(import、alias、be 2020-06-07
- 最强Dubbo面试题,附带超级详细答案 2020-06-06
- 学习源码的第八个月,我成了Spring的开源贡献者 2020-06-02
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash