前言
Dubbo的Filter实现入口是在ProtocolFilterWrapper,因为ProtocolFilterWrapper是Protocol的包装类,所以会在加载的Extension的时候被自动包装进来,实现在ProtocolFilterWrapper.buildInvokerChain方法
源码解析
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
//获得所有符合条件的Filter(已经排好序的)
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (!filters.isEmpty()) {
//构建filter调用链
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
复制代码
- 获得所有符合条件的Filter(已经排好序的)
- 构建filter调用链
List filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
public List<T> getActivateExtension(URL url, String key, String group) {
//key service.filter
//获取该service自定义filter,以逗号隔开
String value = url.getParameter(key);
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
复制代码
public List<T> getActivateExtension(URL url, String[] values, String group) {
List<T> exts = new ArrayList<T>();
//自定义filter数组
List<String> names = values == null ? new ArrayList<String>(0) : Arrays.asList(values);
//如果自定义filter包含"-default"就不会加载dubbo自带的filter
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
//通过ExtensionLoader获取所有的filter
getExtensionClasses();
//遍历具有Activate注解的,这个cachedActivates是在getExtensionClasses()中放入的
//dubbo自带的filter都是Activate注解的,不需要手动在配置文件中加入这些filter,
//如果自己也想写全局的,也可以加上Activate注解,这样也会出现在cachedActivates中
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
String name = entry.getKey();
Activate activate = entry.getValue();
//判断是否符合条件 是否在该组中,分为provider和consumer
if (isMatchGroup(group, activate.group())) {
//得到该filter实例
T ext = getExtension(name);
if (!names.contains(name)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)
&& isActive(activate, url)) {
//如果自定义filter不包含该filter,
//并且自定义filter不包含“-”+该filter,并且isActive就放入exts中
//这里如果不想用dubbo自带的某个filter就可以用“-”+filter名称去除掉
exts.add(ext);
}
}
}
//对这些filter进行排序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
List<T> usrs = new ArrayList<T>();
//遍历自定义filter数组
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
//如果filter名称不以-开头并且filter数组不包含“-”+该filter名称
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX)
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) {
//如果该名称为default
if (Constants.DEFAULT_KEY.equals(name)) {
//如果exts不为空
if (!usrs.isEmpty()) {
//把usrs加入到exts中并放到前面,并清空usrs,
//这里和后面实现了自定义filter和dubbo自带的filter排序功能
//因为看后面最终返回的是exts(有序的)
//举例 filter="filter1,filter2,filter3,default,filter4"
//最终exts顺序就为filter1,filter2,filter3,dubbo符合条件的filter,filter4
//如果没有写default,dubbo自带的会在自定义的前面
exts.addAll(0, usrs);
, usrs.clear();
}
} else {
//如果不包含就放入usrs中
T ext = getExtension(name);
usrs.add(ext);
}
}
}
//变量完后,把usrs剩下的都放到exts中
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
复制代码
private Map<String, Class<?>> getExtensionClasses() {
//从缓存中获取
Map<String, Class<?>> classes = cachedClasses.get();
if (classes == null) {
synchronized (cachedClasses) {
classes = cachedClasses.get();
if (classes == null) {
//如果没有就从文件中加载
classes = loadExtensionClasses();
//放到缓存中
cachedClasses.set(classes);
}
}
}
return classes;
}
private Map<String, Class<?>> loadExtensionClasses() {
//获取该类型的SPI默认值
final SPI defaultAnnotation = type.getAnnotation(SPI.class);
if (defaultAnnotation != null) {
String value = defaultAnnotation.value();
if ((value = value.trim()).length() > 0) {
String[] names = NAME_SEPARATOR.split(value);
if (names.length > 1) {
throw new IllegalStateException("more than 1 default extension name on extension " + type.getName()
+ ": " + Arrays.toString(names));
}
//设置默认值
if (names.length == 1) cachedDefaultName = names[0];
}
}
Map<String, Class<?>> extensionClasses = new HashMap<String, Class<?>>();
//META-INF/dubbo/internal/ 该文件下查找 com.alibaba.dubbo.rpc.Filter并加载
loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY);
//META-INF/dubbo/ 该文件下查找 com.alibaba.dubbo.rpc.Filter
loadDirectory(extensionClasses, DUBBO_DIRECTORY);
//META-INF/services/ 该文件下查找 com.alibaba.dubbo.rpc.Filter
loadDirectory(extensionClasses, SERVICES_DIRECTORY);
//最终Map<String, Class<?>> extensionClasses
return extensionClasses;
}
复制代码
使用和小结
综上源码解析,dubbo是采用自己的SPI机制,一个serviceBean会被很多层东西包装,其中包含ProtocolFilterWrapper
在转换为export的时候,会调用
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
复制代码
构建filter链
dubbo的filter也是基于SPI机制,看上面代码可以看到他会去 META-INF/dubbo/internal/、META-INF/dubbo/、META-INF/services/ 该文件下查找 com.alibaba.dubbo.rpc.Filter并加载
- 我们要自定义filter 可以在resources下创建对应文件夹和文件,然后在对应的service上面加上filter="hystrixFilter"
- 如果想把自己的filter变成dubbo自带的filter一样,可以在自己的filter类上加上@Activate注解,这样你就可以认为他是全局的filter了,就不需要在每个service中引入了
- 全局的filter可以用注解值 group区分是提供者和调用者这样就更加灵活使用全局的filter了,还可以用order进行排序
- 如果不想用所有dubbo自带的filter或者说全局的filter 可以用-default去除,如果不想用单个dubbo自带的filter,可以用“-”+filter名称去除
- filter排序,一般情况下自定义filter会在dubbo自带的filter后面,可以用filter="filter1,default,filter3"进行排序