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 10011001

两者计算结果为 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 10011

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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vulnhub靶场—matrix-breakout-2-morpheus靶机

一&#xff0c;实验环境 靶机ip&#xff1a;192.168.150.131攻击机ip&#xff1a;192.168.150.130 二&#xff0c;信息收集 arp-scan -l 扫描网段&#xff0c;寻找靶机ip 使用工具nmap进行端口扫描 nmap -A -T4 -p- 192.168.150.131 通过信息收集发现了靶机有80和81这两个…

golang学习笔记——基础01

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

酷柚易汛ERP - 通用设置操作指南

1、系统设置 对系统进行初步设置&#xff0c;如系统LOGO、站点名称、备案号、版权信息、尾部信息及系统相关的一些基础设置 2、应用/小程序配置 对系统移动端进行相关配置 3、短信配置 对系统短信进行配置&#xff0c;此配置用于移动端一些通知类信息发送【目前仅支持阿里云…

centos7 网卡聚合bond0模式配置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、什么是网卡bond二、网卡bond的模式三、配置bond0 一、什么是网卡bond 所谓bond&#xff0c;就是把多个物理网卡绑定成一个逻辑上的网卡&#xff0c;使用同一个…

es使用客户端,“grunt” 不是内部或外部命令,多种解决方法

”grunt“不是内部或外部命令&#xff0c;也不是可运行的程序 或批处理文件。 4、问题排查 查看node的安装根目录 npm root -g 在运行grunt -version还是不行 网上找了很多&#xff0c;给出正确解决方案的没几个&#xff0c;所以自己摸索&#xff0c;最后确定了加环境变量的解…

电子学会C/C++编程等级考试2021年09月(一级)真题解析

C/C++等级考试(1~8级)全部真题・点这里 第1题:数字判断 输入一个字符,如何输入的字符是数字,输出yes,否则输出no 输入 一个字符 输出 如何输入的字符是数字,输出yes,否则输出no 样例1输入 样例1输入 5样例1输出 yes样例2输入 A 样例2输出 …

人生阶段总结

--回顾一下我迷茫、努力、不开心又失败的阶段人生自我介绍一下&#xff0c;我是一个智力平平&#xff0c;记忆力差&#xff0c;适合自学的长睡眠者。 大专之前 国内的应试教育基本上不适合我&#xff0c;厌恶补课厌恶机械式听课刷题&#xff0c;所有的优势学科都是自学&#xf…

23111709[含文档+PPT+源码等]计算机毕业设计基于Spring Boot智能无人仓库管理-进销存储

文章目录 **软件开发环境及开发工具&#xff1a;****功能介绍&#xff1a;****论文截图&#xff1a;****数据库&#xff1a;****实现&#xff1a;****代码片段&#xff1a;** 编程技术交流、源码分享、模板分享、网课教程 &#x1f427;裙&#xff1a;776871563 软件开发环境及…

linux课程第一课------命令的简单的介绍

作者前言 &#x1f382; ✨✨✨✨✨✨&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ​&#x1f382; 作者介绍&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

Java 之拼图小游戏

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

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…

C语言——冒泡排序

一、冒泡排序是什么 冒泡排序&#xff1a; 冒泡排序(Bubble Sort)&#xff0c;又被称为气泡排序或泡沫排序。升序时&#xff1a;它会遍历若干次需要排序的数列&#xff0c;每次遍历时&#xff0c;它都会从前往后依次的比较相邻两个数的大小&#xff1b;如果前者比后者大&#x…