前言
在Dubbo中有Filter使用,对于Filter来说我们会遇到这样的问题,Filter自身有很多的实现,我们希望某种条件下使用A实现,另外情况下使用B实现,这个时候我们前面介绍@SPI和@Adaptive就不能满足我们要求了,这个时候我们就需要使用@Activate。
Activate注解表示一个扩展是否被激活(使用),可以放在类定义和方法上,Dubbo中用它在扩展类定义上,表示这个扩展实现激活条件和时机。
如何使用
-
自定义接口;
publicinterfaceActivateDemo{
/**
*测试
*@parammsg
*@return
*/
Stringtest(Stringmsg);
}
-
实现接口,分别进行默认实现、多个组、排序、从URL获取值,多种方式的案例;
"default"})
publicclassDefaultActivateDemoImplimplementsActivateDemo{
publicStringtest(Stringmsg){
returnmsg;
}
}
(group={"groupA","groupB"})
publicclassComposeGroupActivateDemoImplimplementsActivateDemo{
publicStringtest(Stringmsg){
returnmsg;
}
}
(order=1,group={"order"})
publicclassOrder1ActivateDemoImplimplementsActivateDemo{
publicStringtest(Stringmsg){
returnmsg;
}
}
(order=2,group={"order"})
publicclassOrder2ActivateDemoImplimplementsActivateDemo{
publicStringtest(Stringmsg){
returnmsg;
}
}
(value={"value"},group={"group"})
publicclassValueAndGroupActivateDemoImplimplementsActivateDemo{
publicStringtest(Stringmsg){
returnmsg;
}
}
(group={
-
在resources下新建META-INF/dubbo/internal文件夹,新建自己定义接口的全限定名文件名,名称以及内容可参考以下内容;
-
测试案例;
publicstaticvoidmain(String[]args){
ExtensionLoader<ActivateDemo>loader=ExtensionLoader.getExtensionLoader(ActivateDemo.class);
URLurl=URL.valueOf("test://localhost/test");
List<ActivateDemo>list=loader.getActivateExtension(url,newString[]{},"order");
System.out.println(list.size());
list.forEach(item->System.out.println(item.getClass()));
}
publicstaticvoidmain(String[]args){
ExtensionLoader<ActivateDemo>loader=ExtensionLoader.getExtensionLoader(ActivateDemo.class);
URLurl=URL.valueOf("test://localhost/test");
//注意这里要使用url接收,不能直接url.addParameter()
url=url.addParameter("value","test");
List<ActivateDemo>list=loader.getActivateExtension(url,newString[]{"order1","default"},"group");
System.out.println(list.size());
list.forEach(item->System.out.println(item.getClass()));
}
源码分析
@Activate注解标注在扩展实现类上,有 group、value 以及 order 三个属性,三个属性作用如下:
-
group 修饰的实现类可以列举为一种标签,标签用来区分是在 Provider 端被激活还是在 Consumer 端被激活; -
value 修饰的实现类只在 URL 参数中出现指定的 key 时才会被激活; -
order 用来确定扩展实现类的排序;
(RetentionPolicy.RUNTIME)
({ElementType.TYPE,ElementType.METHOD})
public Activate{
String[]group()default{};
String[]value()default{};
String[]before()default{};
String[]after()default{};
intorder()default0;
}
SPI在扩展类加载时候, loadClass() 方法会对 @Activate的注解类进行扫描,其中会将包含 @Activate 注解的实现类缓存到 cachedActivates 一个Map集合中,Key为扩展名,Value为@Activate注解;
privatevoidloadClass(Map<String,Class<?>>extensionClasses,java.net.URLresourceURL,Class<?>clazz,Stringname,
booleanoverridden)throwsNoSuchMethodException{
if(!type.isAssignableFrom(clazz)){
thrownewIllegalStateException("Erroroccurredwhenloadingextensionclass(interface:"+
type+",classline:"+clazz.getName()+"),class"
+clazz.getName()+"isnotsubtypeofinterface.");
}
//判断类是否加载Adaptive注解
if(clazz.isAnnotationPresent(Adaptive.class)){
cacheAdaptiveClass(clazz,overridden);
//是否是扩展类,是的话就加入cachedWrapperClasses属性
}elseif(isWrapperClass(clazz)){
cacheWrapperClass(clazz);
}else{
//检测是否有默认构造起
clazz.getConstructor();
if(StringUtils.isEmpty(name)){
//未配置扩展名,自动生成,主要用于兼容javaSPI的配置。
name=findAnnotationName(clazz);
if(name.length()==0){
thrownewIllegalStateException(
"Nosuchextensionnamefortheclass"+clazz.getName()+"intheconfig"+resourceURL);
}
}
//获得扩展名,可以是数组,有多个拓扩展名。
String[]names=NAME_SEPARATOR.split(name);
if(ArrayUtils.isNotEmpty(names)){
//如果是自动激活的实现类,则加入到缓存
cacheActivateClass(clazz,names[0]);
for(Stringn:names){
//存储Class到名字的映射关系
cacheName(clazz,n);
//存储名字到Class的映射关系
saveInExtensionClass(extensionClasses,clazz,n,overridden);
}
}
}
}
使用cachedActivates这个集合的地方是 getActivateExtension() ,关于此方法有4个重载函数,核心方法包含三个重要参数,URL中包含了配置信息,Values是配置中指定的扩展名,Group标签,下面是getActivateExtension的核心逻辑,首先就是获取默认的扩展集合,其次将扩获取到扩展类放到一个有序的集合中,按照顺序添加自定义扩展类的实现。
publicList<T>getActivateExtension(URLurl,Stringkey){
returngetActivateExtension(url,key,null);
}
publicList<T>getActivateExtension(URLurl,String[]values){
returngetActivateExtension(url,values,null);
}
publicList<T>getActivateExtension(URLurl,Stringkey,Stringgroup){
Stringvalue=url.getParameter(key);
returngetActivateExtension(url,StringUtils.isEmpty(value)?null:COMMA_SPLIT_PATTERN.split(value),group);
}
publicList<T>getActivateExtension(URLurl,String[]values,Stringgroup){
List<T>activateExtensions=newArrayList<>();
//solvethebugofusing@SPI'swrappermethodtoreportanullpointerexception.
//TreeMap进行排序
TreeMap<Class,T>activateExtensionsMap=newTreeMap<>(ActivateComparator.COMPARATOR);
Set<String>loadedNames=newHashSet<>();
//传入的数组包装成为set
Set<String>names=CollectionUtils.ofSet(values);
//包装好的数据中判断不含"-default""
if(!names.contains(REMOVE_VALUE_PREFIX+DEFAULT_KEY)){
//获取所有的加载类型
getExtensionClasses();
//cachedActivate存储被@Activate修饰类型
for(Map.Entry<String,Object>entry:cachedActivates.entrySet()){
Stringname=entry.getKey();
Objectactivate=entry.getValue();
String[]activateGroup,activateValue;
//兼容老的逻辑
if(activateinstanceofActivate){
activateGroup=((Activate)activate).group();
activateValue=((Activate)activate).value();
}elseif(activateinstanceofcom.alibaba.dubbo.common.extension.Activate){
activateGroup=((com.alibaba.dubbo.common.extension.Activate)activate).group();
activateValue=((com.alibaba.dubbo.common.extension.Activate)activate).value();
}else{
continue;
}
//判断group是否匹配
if(isMatchGroup(group,activateGroup)
//没有出现在values配置中的,即为默认激活的扩展实现
&&!names.contains(name)
//通过"-"明确指定不激活该扩展实现
&&!names.contains(REMOVE_VALUE_PREFIX+name)
//检测URL中是否出现了指定的Key
&&isActive(activateValue,url)
//去重判断
&&!loadedNames.contains(name)){
//筛入treeMap中
activateExtensionsMap.put(getExtensionClass(name),getExtension(name));
loadedNames.add(name);
}
}
if(!activateExtensionsMap.isEmpty()){
activateExtensions.addAll(activateExtensionsMap.values());
}
}
List<T>loadedExtensions=newArrayList<>();
for(Stringname:names){
//排除对应扩展名不包含以-开始以及一+name
if(!name.startsWith(REMOVE_VALUE_PREFIX)
&&!names.contains(REMOVE_VALUE_PREFIX+name)){
if(!loadedNames.contains(name)){
if(DEFAULT_KEY.equals(name)){
if(!loadedExtensions.isEmpty()){
activateExtensions.addAll(0,loadedExtensions);
loadedExtensions.clear();
}
}else{
//获取对应名字扩展
loadedExtensions.add(getExtension(name));
}
loadedNames.add(name);
}else{
//IfgetExtension(name)exists,getExtensionClass(name)mustexist,sothereisnonullpointerprocessinghere.
StringsimpleName=getExtensionClass(name).getSimpleName();
logger.warn("Catchduplicatedfilter,ExtensionLoaderwillignoreoneofthem.Pleasecheck.FilterName:"+name+
".IgnoredClassName:"+simpleName);
}
}
}
if(!loadedExtensions.isEmpty()){
activateExtensions.addAll(loadedExtensions);
}
returnactivateExtensions;
}
结束
欢迎大家点点关注,点点赞!
© 版权声明
文章版权归作者所有,未经允许请勿转载。
版权声明:
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com
1、IT大王遵守相关法律法规,由于本站资源全部来源于网络程序/投稿,故资源量太大无法一一准确核实资源侵权的真实性;
2、出于传递信息之目的,故IT大王可能会误刊发损害或影响您的合法权益,请您积极与我们联系处理(所有内容不代表本站观点与立场);
3、因时间、精力有限,我们无法一一核实每一条消息的真实性,但我们会在发布之前尽最大努力来核实这些信息;
4、无论出于何种目的要求本站删除内容,您均需要提供根据国家版权局发布的示范格式
《要求删除或断开链接侵权网络内容的通知》:https://itdw.cn/ziliao/sfgs.pdf,
国家知识产权局《要求删除或断开链接侵权网络内容的通知》填写说明: http://www.ncac.gov.cn/chinacopyright/contents/12227/342400.shtml
未按照国家知识产权局格式通知一律不予处理;请按照此通知格式填写发至本站的邮箱 wl6@163.com