0
点赞
收藏
分享

微信扫一扫

优雅编码之——传统项目中,使用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的方式。

只需要在请求的参数中包含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

OpenFeign

feign

举报

相关推荐

0 条评论