Alibaba Dubbo是开源的分布式服务治理框架,提供了服务注册,服务发现,动态配置和路由的功能。
Spring自定义标签
通过自定义标签,跟Spring无缝结合,自定义标签需要两个部分:在项目的resource/META-INF包里面配置2个文件spring.handlers和spring.schemas
spring.handlers 的内容:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
定义了解析标签的类com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
spring.schemas的内容:
http\://code.alibabatech.com/schema/dubbo/dubbo.xsd=META-INF/dubbo.xsd
用一个url来映射我们配置好的文件
dubbo.xsd的内容:
DubboNamespaceHandler继承了Spring的NamespaceHandlerSupport接口 init方法注册了各个标签的解析器 ,根据解析生成对应的BeanDefinition,这样就可以在Spring中使用自定义的标签
扩展点
JDK的ServiceLoader
JDK提供给我们一套扩展点机制,通过ServiceLoader遍历所有jar查找META-INF/services目录下实现某个接口的实现类,现在以Spark中的一个例子说明
这里就用ServiceLoader加载所有的DataSourceRegister然后判断每个对象的shortName()是否给定的值,这样我们可以实现自己的数据源。
Dubbo的ExtensionLoader
我们一般用ExtensionLoader.getExtensionLoader(***.class)开始获取扩展点。
type 必须不为空且为接口还得有SPI注解,先从缓存中有该接口对应的ExtensionLoader,如果没有的话new一个 ,然后放进缓存中。返回这个ExtensionLoader。
看一下实例化的过程:
会用到ExtensionFactory接口的Adaptive实现 看一下getAdaptiveExtension方法
先判断是否已经存在,然后加锁再判断,如果依旧不存在,调用createAdaptiveExtension方法生成一个并缓存。
cachedClasses定义:
如果还没有设置则调用loadExtensionClasses方法加载
先判断SPI注释中参数不能大于1,赋值给成员变量cachedDefaultName,作为缺省的实现
loadFile方法中三个常数如下:
看一下loadFile:
先读取文件中的内容
根据配置反射得出clazz
clazz必须是type的实现类,如果有Adaptive注解则判断cachedAdaptiveCalss是否为空,如果为空,赋值给cachedAdaptiveCalss,否则系统中会有两个cachedAdaptiveCalss,抛出异常提示。
这里根据type接口作为参数找到一个包装类,如果有则加入wrapper这个Set中,这里的ConcurrentHashSet是Dubbo自己根据JDK的ConcurrentHashMap实现的
如果不是包装类
这里主要是给cachedActivities加入有Activite注解的类,并且映射class和name以及name和class。
返回前面getAdaptiveExtensionClass方法,如果我们前面缓存了cachedAdaptiveClass就直接返回cachedAdaptiveClass。调用injectExtension方法进行注入:
通过反射给set调用set方法赋值。
如果没有cachedAdaptiveClass则通过字节码生成器生成
需要至少有一个方法有Adaptive注解
实现的思想是根据方法的参数URL,Invocation等不同加载对同的扩展点
比如:Protocol生成以下:
class Protocol$Adpative implements Protocol{
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException{
if (arg0 == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invokerargument == null");
}
if (arg0.getUrl() == null) {
throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");
}
com.alibaba.dubbo.common.URLurl = arg0.getUrl();
StringextName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) {
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocolextension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0,com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException{
if (arg1 == null) {
throw new IllegalArgumentException("url == null");
}
com.alibaba.dubbo.common.URLurl = arg1;
StringextName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) {
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
}
com.alibaba.dubbo.rpc.Protocolextension = (com.alibaba.dubbo.rpc.Protocol)com.alibaba.dubbo.common.ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
}
看一下getExtension
如果缓存有则返回,没有则调用createExtension生成一个在缓存
先对该name的扩展点实例化,然后判断时候有Wrapper的包装类,如果有,则一层层包装,最后返回一个包装类,而不是name的扩展点实例,这里我们可以实现自己的逻辑。