当前位置: 首页 > news >正文

优雅编码之——传统项目中,使用openfeign替换掉项目中的httpclient

使用spring cloud时,当遇到服务与其他服务调用的场景,一般都会使用spring cloud openfeign来进行调用。

通过@feign注解让代码变得非常简单而又优雅,即便是跨服务的调用,写起来就像项目内部自己调自己的方法一样,之顺畅~

但当项目是非spring cloud项目,在服务内调用外部的http服务时,可能首先想到的就是httpclient或okhttp

将httpclient封装为一个工具类,哪里用到就拿出来调哪里,将请求url和参数信息传入,再将请求和响应的前后加上编解码的处理代码

如果项目中又遇到需要调很多这样接口的地方,又遇到各种请求头的适配、请求method的适配、参数类型的适配……,工具类的代码可能都能封装出好几十个方法来,代码看起来可能还是会有点别扭丑丑的~

那么有没有什么更好的方法来解决上面这一问题呢,答案是必须有的:feign就是专门干这个事的

正如feign官网所说的那样:Feign makes writing java http clients easier

feign


为了能让大家在传统项目中一起了解下feign这个组件,这里特意将feign快速引入到传统项目中的流程进行简单记录一下。


引入方式

以maven项目为例,添加pom依赖

    <properties>
        <openfeign.version>11.9.1</openfeign.version>
    </properties>
    
    <dependencies>
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-core</artifactId>
        </dependency>
        <!--序列化方式支持gson、Jackson、fastjson、Sax等-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-gson</artifactId>
        </dependency>
         <!--可选项,slf4j日志支持-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-slf4j</artifactId>
        </dependency>
    </dependencies>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.github.openfeign</groupId>
                <artifactId>feign-bom</artifactId>
                <version>${openfeign.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

其中openfeign.version为选择的feign版本,最新的版本可直接从feign的github中获取

请求配置

普通请求

import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import feign.Feign;
import feign.Logger;
import feign.Request;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.TimeUnit;

@Configuration
public class ApiRegisterConf {
    private final String testApiUrl = "http://127.0.0.1:8080";

    @Bean
    public TestApiService testApiService() {
        return Feign.builder()
                .decoder(new GsonDecoder())
                .encoder(new GsonEncoder())
                .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
                //需要引入feign-slf4j依赖
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL)
                .target(TestApiService.class, testApiUrl);
    }
}

其中TestApiService为:

import com.haiyang.javastu.springtransactionmanager.model.JsonResult;
import com.haiyang.javastu.springtransactionmanager.model.TestInfo;
import com.haiyang.javastu.springtransactionmanager.model.TestInfoResult;
import feign.Headers;
import feign.Param;
import feign.RequestLine;


public interface TestApiService {

    @Headers("Content-Type: application/json")
    @RequestLine("POST /hello/test.do")
    JsonResult<TestInfoResult> helloTest(TestInfo testInfo);
}

写个controller测试一下:

import com.haiyang.javastu.springtransactionmanager.model.JsonResult;
import com.haiyang.javastu.springtransactionmanager.model.TestInfo;
import com.haiyang.javastu.springtransactionmanager.model.TestInfoResult;
import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping(value = "test/feign/")
@RestController
public class TestFeignController {
    private final TestApiService testApiService;

    @Autowired
    public TestFeignController(TestApiService testApiService) {
        this.testApiService = testApiService;
    }

    @PostMapping(value = "helloTest")
    public JsonResult<TestInfoResult> helloTest(@RequestBody TestInfo testInfo) {
        return testApiService.helloTest(testInfo);
    }

}

发个请求测试一下

POST http://localhost:8080/test/feign/helloTest
Content-Type: application/json

{"id": 123}

输出结果:

{
  "msg": null,
  "code": 200,
  "data": {
    "id": 123,
    "name": "zhangsan",
    "age": 27,
    "hobbys": [
      "programing",
      "reading"
    ]
  }
}

result

如果想要输出feign的请求详细日志,记得需要将feign包的日志级别设为DEBUG级别

logging:
  level:
      feign: debug

请求携带自定义header

feign提供了requestInterceptor来实现对请求的拦截处理,如果遇到需要进行token之类的header透传的场景,用它来实现就可以了。

示例:

import com.haiyang.javastu.springtransactionmanager.service.TestApiService;
import feign.*;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.slf4j.Slf4jLogger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Configuration
public class ApiRegisterConf {
    private final String testApiUrl = "http://127.0.0.1:8080";

    @Bean
    public TestApiService testApiService() {
        return Feign.builder()
                .decoder(new GsonDecoder())
                .encoder(new GsonEncoder())
                .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL)
                //自定义请求头
                .requestInterceptor(new MyHeaderRequestInterceptor())
                .target(TestApiService.class, testApiUrl);
    }

    public static class MyHeaderRequestInterceptor implements RequestInterceptor {

        @Override
        public void apply(RequestTemplate template) {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            //添加token,设置进请求头
            template.header("token", request.getHeader("token"));
        }
    }
}

写个测试请求测试一下

result

动态请求host

有时需要根据业务需要,可能需要向不同host发起请求。在feign中也提供了动态切换host的方式。

Overriding the Request Line

If there is a need to target a request to a different host then the one supplied when the Feign client was created, or you want to supply a target host for each request, include a java.net.URI parameter and Feign will use that value as the request target.

@RequestLine("POST /repos/{owner}/{repo}/issues")
void createIssue(URI host, Issue issue, @Param("owner") String owner, >@Param("repo") String repo);

只需要在请求的参数中包含URI参数就可以进行host的自定义了。

如上面的示例中,改成这样:

public interface TestApiService {

    @Headers("Content-Type: application/json")
    @RequestLine("POST /hello/test.do")
    JsonResult<TestInfoResult> helloTest(URI hostUri, TestInfo testInfo);
}

调用的地方将uri传入即可:

    @PostMapping(value = "helloTest")
    public JsonResult<TestInfoResult> helloTest(@RequestBody TestInfo testInfo) {
        URI uri = URI.create("http://192.168.1.4:8080");
        return testApiService.helloTest(uri, testInfo);
    }

其调用效果也是一样的。
result

spring cloud openfeign、openfeign、feign的区别

spring cloud openfeign

This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.

OpenFeign

Ultimately, Netflix decided to stop using Feign internally and ceased its development. As a result of this decision, Netflix fully transferred Feign to the open-source community under a new project named OpenFeign.

feign

Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign’s first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.

相关文章:

  • 【ENVI精讲】处理专题五:基于像元二分模型的植被覆盖度反演
  • Docker 官方镜像Tomcat 无法访问 解决方案
  • Qt使用OpenCv
  • 【JavaEE进阶系列 | 从小白到工程师】泛型的详细介绍使用与类型通配符,直接上手
  • java毕业设计论坛管理系统mybatis+源码+调试部署+系统+数据库+lw
  • k8s对接ceph,ceph-csi方式
  • Kubernetes控制平面组件:Kubelet
  • 忘记背后,努力面前【开学季flag】
  • 2022-09-04 瞒春 学习笔记
  • 谷歌?亲斤手不推荐 选它就对了
  • Ubuntu20.04下载opencv3.4--未完善
  • (待修改)PyG安装步骤
  • vue2中vant适配-把px都换算成rem
  • 猿创征文|Spring5梦开始的地方:入门必看
  • [中秋特别定制版本]绝美登录页面搭配[登录数据存储到服务器](服务器宝塔数据库开通+短信服务开通+后端redis验证码缓存)
  • IE9 : DOM Exception: INVALID_CHARACTER_ERR (5)
  • 【Redis学习笔记】2018-06-28 redis命令源码学习1
  • CentOS6 编译安装 redis-3.2.3
  • Docker: 容器互访的三种方式
  • Python - 闭包Closure
  • Ruby 2.x 源代码分析:扩展 概述
  • Unix命令
  • 大数据与云计算学习:数据分析(二)
  • 大型网站性能监测、分析与优化常见问题QA
  • 聊聊redis的数据结构的应用
  • 事件委托的小应用
  • 我感觉这是史上最牛的防sql注入方法类
  • 一、python与pycharm的安装
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • ​LeetCode解法汇总2304. 网格中的最小路径代价
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #Ubuntu(修改root信息)
  • #免费 苹果M系芯片Macbook电脑MacOS使用Bash脚本写入(读写)NTFS硬盘教程
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (二)Eureka服务搭建,服务注册,服务发现
  • (循环依赖问题)学习spring的第九天
  • (一一四)第九章编程练习
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .Net 8.0 新的变化
  • .NET 常见的偏门问题
  • .net 中viewstate的原理和使用
  • .NET/C# 判断某个类是否是泛型类型或泛型接口的子类型
  • .Net小白的大学四年,内含面经
  • [2016.7 test.5] T1
  • [android学习笔记]学习jni编程
  • [Asp.net mvc]国际化
  • [BZOJ]4817: [Sdoi2017]树点涂色
  • [C# WPF] 如何给控件添加边框(Border)?
  • [codeforces] 25E Test || hash
  • [Django ]Django 的数据库操作
  • [Leetcode] 寻找数组的中心索引
  • [linux] 创建用户