OpenFeign框架原生应用(一)简单示例

SpringCloud OpenFeign
placeholder image
admin 发布于:2024-07-26 09:36:31
阅读:loading

通常对于OpenFeign技术的使用都是通过`spring-cloud-starter-openfeign`的方式引用的,需要依赖spring-cloud相关的组件,我个人也是在学习Spring Cloud Alibaba相关技术体系时,总结过两篇关于OpenFeign的应用示例。如果想丝滑的使用OpenFeign而又不想在项目中集成SpringCloud相关技术体系时(企业工作中总会有一些不可与之制约的因素),可以尝试使用OpenFeign的原生应用,仍然是非常丝滑的去使用。本文将使用OpenFeign的原生应用实现接口的调用示例。

1.实现过程

1.1 Maven坐标依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.7.18</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
    <version>2.7.18</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.7.18</version>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.7.18</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.34</version>
</dependency>

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.13.0</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.1</version>
    <scope>test</scope>
</dependency>


<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>3.14.9</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
    <version>${feign.version}</version>
    <exclusions>
        <exclusion>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
    <version>${feign.version}</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-jackson</artifactId>
    <version>${feign.version}</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-spring4</artifactId>
    <version>13.2.1</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-slf4j</artifactId>
    <version>${feign.version}</version>
    <exclusions>
        <exclusion>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>3.8.0</version>
</dependency>

特别说明:

(1)截至当前(2024-07)OpenFeign最新版本为13.3,所以本次使用最新版本进行实践,group id为io.github.openfeign;

(2)整体上feign-spring的版本只有最新版13.3,该版本基于JDK17编译,不支持JDK8,需使用feign-spring4依赖,后者是支持JDK8和Spring4.X的版本13.2.1;

(3)本次使用的SpringBoot版本为当前最新2.X的版本,引入的logback与feign-slf4j的13.3版本依赖的logback版本冲突,故排除feign-slf4j对logback的依赖,使用SpringBoot框架的依赖;

(4)最新版feign-okhttp依赖的okhttp版本为4.1X的版本,版本太高,jar包中不完全是class文件,无法正常使用,所以使用支持JDK8的版本,降级到可使用的最高版本3.14.9;

(5)feign-form-spring同样的道理,降级使用到可用的最新版本3.8.0;

PS:小小的坐标依赖,如果想要非常合理的依赖,其内部还是有很大的学问的。

1.2.声明组件类

(1)定义基于Apache的HttpClient配置类,声明连接池的配置、超时的参数等;

(2)定义基于Okhttp的HttpClient配置类,声明链接参数信息、超时的参数等;

(3)定义参数传递的编码、解码和错误处理类,选用按照Spring MVC的规范调用,集成Spring提供的组件实现;

(4)定义Controller接口,提供一些测试的接口生产者;

PS:Apache与Okhttp的底层框架是二选一的,一般在实际中选定一个组件来使用。

2.简单示例

本次提供一些简单点的基础示例,主要是使用OpenFeign的原生调用API的演示,包含构造接口的代理实现过程和Spring MVC规范的接口传参数,有:@RequestParam、@RequestBody、@PathVariable、@RequestPart等;同时示例包含@GetMapping、@PostMapping、@PutMapping等请求方式,参考代码如下:

2.1 Controller定义API服务端

import ...;

/**
 * 简单Api测试
 * @author chendd
 */
@RestController
@RequestMapping(value = "/api/simple" , consumes = MediaType.ALL_VALUE , produces = MediaType.APPLICATION_JSON_VALUE)
public class SimpleApiController {

    @GetMapping(value = "/hello")
    public String hello() {
        return "world";
    }

    @GetMapping(value = "/hello/{name}")
    public String hello(@PathVariable String name) {
        return "hello:" + name;
    }

    @PostMapping(value = "/hello/{id}")
    public Triple<String , String , String> hello(@PathVariable String id ,
                                                  @RequestParam("name") String name ,
                                                  String website) {
        return Triple.of(id , name , website);
    }

    @PutMapping(value = "/hello")
    public Point hello(@RequestBody Point param) {
        return param;
    }

}

2.2 定义SimpleHttpClient客户端

import ...;

/**
 * 简单的接口调用
 * @author chendd
 */
public interface SimpleHttpClient {

    /**
     * 无参数
     */
    @GetMapping(value = "/api/simple/hello" , produces = MediaType.APPLICATION_JSON_VALUE)
    String sayHello();

    /**
     * 1个参数
     */
    @GetMapping(value = "/api/simple/hello/{name}")
    String sayHello(@PathVariable("name") String name);

    /**
     * 3个参数【拼接在URL上传递的参数】
     */
    @PostMapping(value = "/api/simple/hello/{id}")
    String sayHello(@PathVariable("id") String id , @RequestParam("name") String name , @RequestParam("website") String website);

    /**
     * 3个参数【拼接在URL上传递的参数】
     */
    @PutMapping(value = "/api/simple/hello")
    String sayHello(@RequestBody Point param);

}

2.3 定义简单接口的测试类

import ...;

/**
 * 接口测试【简单】
 * @author chendd
 */
@RunWith(JUnit4.class)
public class SimpleHttpClientTest {

    private SimpleHttpClient httpClient;

    @Before
    public void init() {
        ObjectMapper objectMapper = new ObjectMapper();
        final Feign.Builder builder = new Feign.Builder()
                .contract(new SpringContract())
                //.client(new OkHttpClient(OkHttpClientConfig.getHttpClient()))
                .client(new ApacheHttpClient(HttpClientConfig.getHttpClient()))
                .encoder(new HttpFeignFormEncoder(objectMapper))
                .decoder(new HttpFeignFormDecoder(objectMapper))
                .errorDecoder(new HttpFeignFormError())
                .logger(new Slf4jLogger())
                .logLevel(Logger.Level.FULL);
        httpClient = builder.target(SimpleHttpClient.class, "http://127.0.0.1:8080");
    }

    @Test
    public void hello0ArgsTest() {
        System.out.println("===调用无参数返回String接口===");
        httpClient.sayHello();
    }

    @Test
    public void hello1ArgsTest() {
        System.out.println("===调用@PathVariable参数返回String接口===");
        httpClient.sayHello("chendd");
    }

    @Test
    public void hello3ArgsTest() {
        System.out.println("===调用@PathVariable、@RequestParam===");
        httpClient.sayHello("88911" , "陈冬冬" , "https://www.chendd.cn");
    }

    @Test
    public void helloBodyArgsTest() {
        System.out.println("===调用@RequestBody接口===");
        httpClient.sayHello(new Point(88911 , 36));
    }

}

特别说明

(1)init方法是测试接口方法的前置方法,提供了Feigin接口的初始化过程,包含各个参数的处理方式及测试接口的代理类实现;

(2)当前测试类提供的接口测试均为简单的接口,包含常规的多种参数传递和接口响应结果;

(3)示例中包含了使用slf4j来输出日志,并且设置了接口传输过程中的日志信息;

(4)未设置拦截器的其它功能点;

2.4 项目代码结构图

image.png

3.运行示例

项目运行需要先使启动server,运行接口服务端,而后运行接口的Junit客户端,参考如下图所示:

示例演示-含水印.gif

4.其它说明

(1)OpenFeign接口中的Spring MVC风格的调用并不是完全与Spring MVC的请求调用一致,反而更加严谨和规范,需要注意更多的细节规范;

(2)理论上@GetMapping是不支持@RequestBody传递参数的,许多浏览器也不支持,关键是Okhttp框架也不支持,源码中存在对此种传参数传输的校验,抛出异常;而Apache的HttpClient表示支持;

(3)本篇是常规基础接口的调用演示,下一篇是对于OpenFeign的进阶演示,包含了文件上传和下载的参数传递方式;

(4)源码工程下载:《源码下载.zip》;


 点赞


 发表评论

当前回复:作者

 评论列表


留言区