目录

不加 default 查看修饰符的和

加 default 查看修饰符的和

以方法不加 default 修饰符来验证逻辑

第一步

第二步

第三步

第四步

以方法加 default 修饰符来验证逻辑

 第二步

第三步

第四步

总结


近期在做项目时给自己挖了一个坑,问题重现如下

使用的组件版本如下

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 还是我的调用方式有问题?

总结

定义的接口类中的方法中,修饰符是否添加有下面的情况

  1. 如果不声明修饰符,默认是 public abstract,对应的值为 1025。
  2. 如果声明修饰符 default,默认是 public,对应的值为 1。

为了防止 feign 调用出现一些其他未知的问题,还是不添加修饰符为好。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐