dubbo系列之SPI机制实现
从这篇博客开始系统学习一下dubbo源码,相信能收获颇丰。
参考
初识SPI
SPI设计目标
面向对象的设计里,模块之间是基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可插拔的原则,如需要替换一种实现,就需要替换代码。为了实现在模块装配的时候,不在模块里面写代码,这就需要一种服务发现机制。java spi就是提供这样的一个机制:为某个接口寻找服务实现的机制。有点类似IOC的思想,就是将装配的控制权移到代码之外。
dubbo SPI和 java SPI的不同之处
1:JDK标准得SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。
2:dubbo SPI中增加了对扩展点IOC和AOP的支持,一个扩展点可以直接setter注入其他扩展点。
dubbo SPI 约定
spi 文件 存储路径 在 META-INF\dubbo\internal 目录下 并且文件名为接口的全路径名 就是=接口的包名+接口名
每个spi 文件里面的格式定义为: 扩展名=具体的类名,例如 dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol。这和jdk SPI的区别就是多了——”扩展名=“,也正是通过这个”扩展名=“,也就是key,实现了只需对key相同的扩展点进行加载。
dubbo SPI的目的:获取一个实现类的对象.
源码构成
运行dubbo源码先找到入口,也就是主函数,dubbo源码的入口就是Main类
Main()——主函数
1 | package com.alibaba.dubbo.container; |
ExtensionLoader类
getExtensionLoader(Class type) ——为该接口new一个ExtensionLoader,然后缓存起来
1 | public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) { |
1 | private ExtensionLoader(Class<?> type) { |
执行以上代码完成了两个属性的初始化
1:每一个ExtensionLoader都包含了两个值type和objectFactory
Class<?> type;//构造器 初始化时要得到的接口名
ExtensionFactory objectFactory// 构造器 初始化时AdaptiveExtensionFactory[SpiExtensionFactory,SpringExtensionFactory]
2:new 一个ExtensionLoader 存储在ConcurrentMap<Class>, ExtensionLoader>> EXTENSION_LOADERS
关于这个objectFactory的一些细节
1:objectFactory就是ExtensionFactory,它也是通过 ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension())来实现的,但是它的objectFactory=null
2:objectFactory的作用:为dubbo的IOC提供所有对象
@Adaptive注解介绍
adaptive注解在类和方法上的区别:
1:注解在类上:代表人工实现编码,即实现了一个装饰类(设计模式中的装饰模式),例如:ExtensionFactory
2:注解在方法上:代表自动生成和编译一个动态的adpative类,例如:Protocol$Adaptive
getAdaptiveExtension()—— 获取一个扩展类,如果@Adaptive注解在类上就是一个装饰类;如果注解在方法上就是一个动态代理类,例如Protocol$Adaptive对象
我们桶Protocol()类进入这个方法,DubboNamespaceHandler类——ServiceBean类——ServiceConfig类,就能看见Protocol()类调用getAdaptiveExtension(),运行DubboDemoProvider类,断点进入getAdaptiveExtension方法
1 | // Dubbo中Protocol的第一行代码 |
1 | public T getAdaptiveExtension() { |
ExtensionLoader.loadFile()——getAdaptiveExtension()调用的方法,用于加载配置文件
目的:通过把配置文件META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol的内容,存储在缓存变量里面。
cachedAdaptiveClass//如果这个class含有adaptive注解就赋值,例如ExtensionFactory,er例如Protocol在这个环节是没有的
cachedWrapperClasses//只有该class无adaptive注解,并且构造函数包含目标接口(type)类型,例如protocol里面的spi就只有ProtocolFilterWrapper和ProtocolListenerWrapper能命中
cachedActivates//剩下的类,包含Axtivate注解
cachedNames//最后剩余的类就存储在这里
ExtensionLoader.createAdaptiveExtensionClass()——自动生成和编译一个动态的adpative类——ProxyFactory$Adaptive,这个类是一个代理类
1 | package com.alibaba.dubbo.rpc; |
getExtension()——获取一个指定对象
1 | public T getExtension(String name) { |