近期在做项目时给自己挖了一个坑,问题重现如下
使用的组件版本如下
spring boot 2.7.15,对应的 spring cloud 版本为 2021.0.5,其中 spring cloud 适配的 openfeign 版本是 3.1.5。
项目中使用的 feign 接口如下
public interface QueryApi {
/**
* 符合查询条件数据总数
* @return
*/
@PostMapping(value = "count")
default CommonResponse count(
@RequestParam(value = "indexName", required = true) @RequestParameterValue String indexName,
@RequestParam(value = "queryFieldConditions", required = false) @Separator List<String> queryFieldConditions
) {
return null;
}
}
乍看没什么问题,只是加了一个 default 关键字,使用 java 8 的特性,java 的 interface 类型中的方法默认是 public abstract 的。
但是到了别的服务调用时就出现问题了,自己之前写的 feign 接口没问题,写法也类似,区别就是加了一个 default 关键字。
想不明白怎么就加了一个关键字就不行了,是哪里的逻辑判断的问题?
本着找问题到底的想法,在方法调用时跟进源码,发现在 ReflectiveFeign 中进了下图中的逻辑
进入 Util.isDefault() 中发现
通过与、或、且、相等四种运算判断了当前方法是否为 default。
写例子看一下加 default 和不加 default 的区别
不加 default 查看修饰符的和
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
public class NoDefaultTest {
public static void main(String[] args) {
System.out.println("Modifier.SYNTHETIC " + 0x00001000);
System.out.println("Modifier.ABSTRACT " + Modifier.ABSTRACT);
System.out.println("Modifier.PUBLIC " + Modifier.PUBLIC);
System.out.println("Modifier.STATIC " + Modifier.STATIC);
int no = (Modifier.PUBLIC | Modifier.STATIC | Modifier.ABSTRACT | 0x00001000);
System.out.println(no);
int temp = 1 & no;
System.out.println(temp);
System.out.println("+++++++++++++++++++++++++++++++++++++++++++");
Class<QueryApi> configurerClass = QueryApi.class;
List<Method> methodList = Arrays.asList(configurerClass.getMethods());
for (Method method : methodList) {
System.out.println(method.getDeclaringClass().isInterface());
System.out.println(method.getModifiers());
}
}
}
执行结果
Modifier.SYNTHETIC 4096
Modifier.ABSTRACT 1024
Modifier.PUBLIC 1
Modifier.STATIC 8
5129
1
+++++++++++++++++++++++++++++++++++++++++++
true
1025
可以发现,不加 default 的方法修饰符值为1025,即 public 和 abstract 的组合。
加 default 查看修饰符的和
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
public class DefaultTest {
public static void main(String[] args) {
System.out.println("Modifier.SYNTHETIC " + 0x00001000);
System.out.println("Modifier.ABSTRACT " + Modifier.ABSTRACT);
System.out.println("Modifier.PUBLIC " + Modifier.PUBLIC);
System.out.println("Modifier.STATIC " + Modifier.STATIC);
int no = (Modifier.PUBLIC | Modifier.STATIC | Modifier.ABSTRACT | 0x00001000);
System.out.println(no);
int temp = 1 & no;
System.out.println(temp);
System.out.println("+++++++++++++++++++++++++++++++++++++++++++");
Class<QueryApi> configurerClass = QueryApi.class;
List<Method> methodList = Arrays.asList(configurerClass.getMethods());
for (Method method : methodList) {
System.out.println(method.getDeclaringClass().isInterface());
System.out.println(method.getModifiers());
}
}
}
执行结果
Modifier.SYNTHETIC 4096
Modifier.ABSTRACT 1024
Modifier.PUBLIC 1
Modifier.STATIC 8
5129
1
+++++++++++++++++++++++++++++++++++++++++++
true
1
可以发现,加 default 的方法修饰符值为1,即 public。
以方法不加 default 修饰符来验证逻辑
判断代码为
((method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC)
&& method.getDeclaringClass().isInterface()
下面拆开来验证
第一步
进行或运算使四个数相加
Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC
最终结果为 5129。接下来进行与运算
第二步
method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)
鉴于上面验证的不加 default 的方法修饰符值为1025,转二进制后为 1001
1 0100 0000 1001
1001
两者计算结果为 1001,接下来与 Modifier.PUBLIC 进行等值比较
第三步
((method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC)
由于 Modifier.PUBLIC 为1,所以两数不相等,后面还要跟当前方法所在类是否是接口进行且运算
第四步
method.getDeclaringClass().isInterface()
鉴于前面的判断为 false,后面为 true,所以结果为 false。
所以 Util.isDefault() 执行结果为 false,执行最后的 else 逻辑。
最终使用 jdk 的动态代理进行调用 MethodHandler 的实现类 SynchronousMethodHandler 来处理请求。
以方法加 default 修饰符来验证逻辑
从第二步来讲
第二步
method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)
鉴于上面验证的加 default 的方法修饰符值为1,转二进制还是 1
1 0100 0000 1001
1
两者计算结果为 1,接下来与 Modifier.PUBLIC 进行等值比较
第三步
((method.getModifiers()
& (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC | SYNTHETIC)) == Modifier.PUBLIC)
由于 Modifier.PUBLIC 为1,所以两数相等,后面还要跟当前方法所在类是否是接口进行且运算
第四步
method.getDeclaringClass().isInterface()
鉴于前面的判断为 true,后面为 true,所以结果为 true。
最终调用 MethodHandler 的实现类 DefaultMethodHandler 来处理请求。
实际在调用 invoke() 的过程中没有执行结果,是 feign 针对 default 修饰的方法的一个 bug 还是我的调用方式有问题?
总结
定义的接口类中的方法中,修饰符是否添加有下面的情况
- 如果不声明修饰符,默认是 public abstract,对应的值为 1025。
- 如果声明修饰符 default,默认是 public,对应的值为 1。
为了防止 feign 调用出现一些其他未知的问题,还是不添加修饰符为好。