dubbo 扩展机制
dubbo 扩展原理
spi(service provider interface)
使用策略模式,一个接口有多个实现类;
接口使用的实现类不在程序中直接指定,由外部配置
java spi加载
一次性加载所有实现类并初始化(有些初始化可能比较耗时,也会造成资源浪费);
java spi加载抛出的异常信息异常可能会被吞掉,导致无法排查出真实出错原因
dubbo spi加载:对java spi做了一些改进
加载所有实现类的类名并缓存,但是没有初始化(初始化后,初始化后的实例也会被缓存);
dubbo扩展加载失败会抛出真是异常并打印日志,部分扩展加载失败不会影响其他扩展的加载;
dubbo扩展增加了ioc(通过setter注入其他扩展)、aop(wrapper包装类包含通用逻辑);
*************
相关注解
@SPI:扩展接口标识
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE}) //标注在类、接口、枚举上,一般标注在接口上,表明该接口是个扩展接口
public @interface SPI {
/**
* default extension name
*/
String value() default ""; //默认使用的扩展实现key(扩展文件中实现类对应的key)
/**
* scope of SPI, default value is application scope.
*/
ExtensionScope scope() default ExtensionScope.APPLICATION;
}
@Adaptive:从url中提取参数,创建动态扩展类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD}) //标注在类、接口、枚举、方法上
//如果标注在实现类上(只能标注在一个实现类上,多个会报错),则将该实现类作为默认使用的拓展实现类,不再动态创建自适应类
//如果标注在方法上,则会从url中提取对应参数,创建自适应类
public @interface Adaptive {
/**
* Decide which target extension to be injected. The name of the target extension is decided by the parameter passed
* in the URL, and the parameter names are given by this method.
* 从url中提取参数,参数的key由value()给出,动态确定要实现的扩展
* <p>
* If the specified parameters are not found from {@link URL}, then the default extension will be used for
* dependency injection (specified in its interface's {@link SPI}).
* 如果url中没有对应的参数(value()方法给出的参数),就会使用(@SPI("default"))指定的扩展
* <p>
* For example, given <code>String[] {"key1", "key2"}</code>:
* <ol>
* <li>find parameter 'key1' in URL, use its value as the extension's name</li>
* <li>try 'key2' for extension's name if 'key1' is not found (or its value is empty) in URL</li>
* <li>use default extension if 'key2' doesn't exist either</li>
* <li>otherwise, throw {@link IllegalStateException}</li>
* </ol>
* value()不为空的情况
* value():{"key1","key2"},
* 先从url中查找key1,如果有key1,提取对用的value作为扩展key
* 如果没有key1,再查找key2,如果有key2,提取对应的value作为扩展key
* 如果没找到,就使用默认扩展(@SPI("default"))作为扩展key,
* 如果@SPI没有指定默认值,则抛出异常
* If the parameter names are empty, then a default parameter name is generated from interface's
* class name with the rule: divide classname from capital char into several parts, and separate the parts with
* dot '.', for example, for {@code org.apache.dubbo.xxx.YyyInvokerWrapper}, the generated name is
* <code>String[] {"yyy.invoker.wrapper"}</code>.
* value()为空的情况
* 根据扩展接口生成默认参数:code org.apache.dubbo.xxx.YyyInvokerWrapper ==> yyy.invoker.wrapper
* 先根据生成的参数名从url中查找,查找到则提取对应的值
* 如果没有,使用默认(@SPI("default"))的扩展实现
* 如果@SPI没有指定默认值,则抛出异常
* @return parameter names in URL
*/
String[] value() default {};
}
@Aativate:默认激活的扩展实现类
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Activate {
/**
* Activate the current extension when one of the groups matches. The group passed into
* {@link ExtensionLoader#getActivateExtension(URL, String, String)} will be used for matching.
*
* @return group names to match
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
String[] group() default {}; //如果数组中的某个值匹配指定的分组值,
//且同时满足value(如果设置了value),则激活
/**
* Activate the current extension when the specified keys appear in the URL's parameters.
* <p>
* For example, given <code>@Activate("cache, validation")</code>, the current extension will be return only when
* there's either <code>cache</code> or <code>validation</code> key appeared in the URL's parameters.
* </p>
*
* @return URL parameter keys
* @see ExtensionLoader#getActivateExtension(URL, String)
* @see ExtensionLoader#getActivateExtension(URL, String, String)
*/
String[] value() default {}; //value不为空时,url中含有value的某个值作为key,且同时满足group,则激活
//value为空时,满足group则激活
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put before the current one
*/
@Deprecated
String[] before() default {}; //已禁用
/**
* Relative ordering info, optional
* Deprecated since 2.7.0
*
* @return extension list which should be put after the current one
*/
@Deprecated
String[] after() default {}; //已禁用
/**
* Absolute ordering info, optional
*
* Ascending order, smaller values will be in the front o the list.
*
* @return absolute ordering info
*/
int order() default 0; //激活排序(数值越小越靠前,默认为0),
//如同时激活多个filter,对filter做先后排序
}
过滤器激活
# ActiveLimitFilter:group为consumer、url中含有actives
@Activate(group = CONSUMER, value = ACTIVES_KEY)
public class ActiveLimitFilter implements Filter, Filter.Listener {
# TimeoutFilter:只需要group为provider就可激活
@Activate(group = CommonConstants.PROVIDER)
public class TimeoutFilter implements Filter, Filter.Listener {
*************
拓展加载
扩展加载说明
每个扩展接口都对应一个extensionLoader缓存对象,没有则创建
# getExtension加载流程
检查name是否为空,为空直接报错;
如果name=true,则创建并返回默认扩展类(@SPI指定的默认扩展类)
根据缓存实现类对应的key从缓存中获取扩展对象,如果没有则创建扩展对象并缓存;
返回创建的扩展实例
# getAdaptiveExtension加载流程
从缓存中获取自适应实现类对象,如果没有则创建自适应类对象;
创建自适应类对象:生成字符串代码,编译器编译成class对象,实例化,初始化、注入扩展依赖;
将创建的自适应对象缓存并返回;
# getActivateExtension加载流程
检查缓存(cachedActivateGroups.size() == 0判断,为0则没有缓存数据),
如果没有缓存数据,则加载所有扩展实现类,将数据缓存(getExtensionClasses());
遍历@Activate集合(cachedActivates),按照匹配规则进行过滤,筛选所有符合条件的激活类;
如果自定义了扩展,可按照自定义的顺序将其添加
如:ext1,default,ext2,顺序为自定义扩展ext1、默认提供的扩展实现类、自定义扩展ext2;
如果自定义的顺序中没有default(ext1,ext2),所有激活类使用activateComparator进行排序;
最后将所有激活类返回
ExtensionLoader:扩展加载类,每个扩展接口都会对应一个ExtensionLoader对象
/**
* {@link org.apache.dubbo.rpc.model.ApplicationModel}, {@code DubboBootstrap} and this class are
* at present designed to be singleton or static (by itself totally static or uses some static fields).
* So the instances returned from them are of process or classloader scope. If you want to support
* multiple dubbo servers in a single process, you may need to refactor these three classes.
* <p>
* Load dubbo extensions
* <ul>
* <li>auto inject dependency extension </li> //如果一个扩展实例A依赖扩展实例B,会自动将B注入A
* <li>au