spring cloud openfeign 使用注意点

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

使用的组件版本如下

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 调用出现一些其他未知的问题,还是不添加修饰符为好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/159356.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

使用express连接MySQL数据库编写基础的增、删、改、查、分页等接口

使用express连接MySQL数据库编写基础的增、删、改、查、分页接口 安装express-generator生成器 cnpm install -g express-generator通过生成器创建项目 express peifang-server切换至serverAPI目录 cd peifang-server下载所需依赖 cnpm install 运行项目 npm start访问项…

echarts 实现同一组legend控制两个饼图示例

实现同一组legend控制两个饼图示例&#xff1a; 该示例有如下几个特点&#xff1a; ①饼图不同值实现分割 ②实现tooltip自定义样式&#xff08;echarts 实现tooltip提示框样式自定义-CSDN博客&#xff09; ③自定义label内容 ④不同值颜色渐变 代码如下&#xff1a; this.o…

CentOS挂载:解锁文件系统的力量

目录 引言1 挂载简介2 挂载本地分区3 挂载网络共享文件系统4 使用CIFS挂载结论 引言 在CentOS&#xff08;一种基于Linux的操作系统&#xff09;上挂载文件系统是一项常见而重要的任务&#xff0c;无论是将新的磁盘驱动器添加到系统&#xff0c;还是挂载网络共享资源&#xff…

一个iOS tableView 滚动标题联动效果的实现

效果图 情景 tableview 是从屏幕顶部开始的&#xff0c;现在有导航栏&#xff0c;和栏目标题视图将tableView的顶部覆盖了 分析 我们为了达到滚动到某个分区选中标题的效果&#xff0c;就得知道 展示最顶部的cell或者区头在哪个分区范围内 所以我们必须首先获取顶部的位置 …

【bigo前端】egret中的对象池浅谈

本文首发于&#xff1a;https://github.com/bigo-frontend/blog/ 欢迎关注、转载。 egret是一款小游戏开发引擎&#xff0c;支持跨平台开发&#xff0c;之前使用这款引擎开发了一款捕鱼游戏&#xff0c;在这里简单聊下再egret中关于对象池的使用&#xff0c;虽然该引擎已经停止…

第六十二周周报

学习目标&#xff1a; 一、实验 二、论文 学习时间&#xff1a; 2023.11.11-2023.11.17 学习产出&#xff1a; 实验 1、CB模块实验效果出来了&#xff0c;加上去效果不太行&#xff0c;后续实验考虑是否将CB模块换到其他地方 2、CiFAR100实验已完成&#xff0c;效果比Vi…

大模型之十二十-中英双语开源大语言模型选型

从ChatGPT火爆出圈到现在纷纷开源的大语言模型&#xff0c;众多出入门的学习者以及跃跃欲试的公司不得不面临的是开源大语言模型的选型问题。 基于开源商业许可的开源大语言模型可以极大的节省成本和加速业务迭代。 当前&#xff08;2023年11月17日)开源的大语言模型如下&#…

基于DE10-Standard Cyclone V SoC FPGA学习---开发板简介

基于DE10-Standard Cyclone V SoC FPGA学习---开发板简介 简介产品规格基于 ARM 的 HPS配置与调试存储器件通讯连接头显示器音频视频输入模数转换器开关、按钮、指示器传感器电源 DE10-Standard 开发板系统框图Connect HTG 组件配置设计资源其他资源 简介 开发板资料 见 DE10-…

什么是CRM管理系统

什么是CRM管理系统 市场竞争的日益激烈&#xff0c;企业对于客户关系的重视程度不断提升。为了更好地管理和维护客户关系&#xff0c;很多企业开始引入CRM&#xff08;Customer Relationship Management&#xff09;管理系统。那么&#xff0c;什么是CRM管理系统呢&#xff1f…

Jenkins代码检测和本地静态检查

1&#xff1a;Jenkins简介 Jenkins是一个用Java编写的开源的持续集成工具&#xff1b;Jenkins自动化部署可以解决集成、测试、部署等重复性的工作&#xff0c;工具集成的效率明显高于人工操作&#xff1b;并且持续集成可以更早的获取代码变更的信息&#xff0c;从而更早的进入测…

Java 之拼图小游戏

声明 此项目为java基础的阶段项目,此项目涉及了基础语法,面向对象等知识,具体像语法基础如判断,循环,数组,字符串,集合等…; 面向对象如封装,继承,多态,抽象类,接口,内部类等等…都有涉及。此项目涉及的内容比较多,作为初学者可以很好的将前面的知识串起来。此项目拿来练手以及…

golang学习笔记——基础01

文章目录 golang概述Go 语言特色Go 语言用途 Go 语言结构执行 Go 程序 Go 语言包管理01Go 语言包管理02Go 语言基础语法Go 标记行分隔符注释标识符字符串连接关键字、预定义标识符Go 语言的空格格式化字符串 Go 语言数据类型数字类型浮点型其他数字类型 Go 语言变量变量声明零值…

Linux下安装部署redis(离线模式)

一、准备工作 1.下载redis的安装包 下载地址&#xff1a;Index of /releases/ 大家可以自行选择redis的版本&#xff0c;笔者选择的是最新的 2.上传到服务器 前提是我先在服务器上创建了一个目录redis7.2.3&#xff0c;我直接上传到这个目录下 二、安装redis 1.解压redis t…

03-瑞吉外卖关于菜品/套餐分类表的增删改查

新增菜品/套餐分类 页面原型 当我们在后台系统中添加菜品/套餐时,需要选择一个菜品/套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐 第一步: 用户点击确定按钮执行submitForm函数发送Ajax请求,将新增菜品/套餐表单中输入的数据以json形式提交给服务端,…

代码随想录算法训练营第24天|77. 组合

JAVA代码编写 77. 组合 给定两个整数 n 和 k&#xff0c;返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [[2,4],[3,4],[2,3],[1,2],[1,3],[1,4], ]示例 2&#xff1a; 输入…

IIC协议保姆级教学

目录 1.IIC协议概述 2.IIC总线传输 3.IIC-51单片机应用 1.起始信号 2.终止信号 3.应答信号 4.数据发送 4.IIC-32单片机应用 用到的库函数&#xff1a; 1.IIC协议概述 IIC全称Inter-Integrated Circuit (集成电路总线)是由PHILIPS公司在80年代开发的两线式串行总线&am…

hive sql 取当周周一 str_to_date(DATE_FORMAT(biz_date, ‘%Y%v‘), ‘%Y%v‘)

select str_to_date(DATE_FORMAT(biz_date, %Y%v), %Y%v)方法拆解 select DATE_FORMAT(now(), %Y%v), str_to_date(202346, %Y%v)

和鲸科技创始人范向伟受邀出席“凌云出海,来中东吧”2023华为云上海路演活动

11月9日&#xff0c;华为云“凌云出海&#xff0c;来中东吧”系列路演活动第二场在上海正式开启。聚焦“创业全球化”&#xff0c;本次活动由华为云携手阿布扎比投资办公室&#xff08;ADIO&#xff09;举办&#xff0c;旨在与渴望出海发展的优秀创业者们共探出海中东新商机。 …

qt 重载信号,使用““方式进行connect()调用解决方案

问题 在Qt中&#xff0c;重载的信号默认是无法使用&这种方式调用的。 因为&只能绑定到一个具体的信号&#xff0c;而重载的信号名称相同&#xff0c;编译器无法确定要绑定哪一个信号。 解决方案 如果非要使用&绑定重载的信号&#xff0c;可以使用函数指针进行转…

元宇宙3D云展厅应用到汽车销售的方案及特点

为了紧紧抓住年轻消费者的需求&#xff0c;汽车销售行业也正在经历一场深刻的变革。在这个变革的前沿&#xff0c;元宇宙3D汽车展厅作为一项全新技术闪亮登场&#xff0c;打破了传统汽车销售模式的限制&#xff0c;为消费者带来了前所未有的购车体验。 元宇宙3D汽车展厅采用了尖…