java 单体应用如何优雅的使用 fegin

本文详细介绍了Feign在非微服务的单体应用中的使用方法,包括引入依赖、创建FeignClientFactory、配置自定义encoder、五种常见使用场景(表单传参、Pojo、Map、Json带Header、返回POJO对象)的示例代码,帮助开发者理解如何在单体应用中灵活运用Feign进行服务调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

用过Spring Cloud的朋友都知道用 fegin 很香,甚至后来者居上的Spring Cloud Alibaba,仍然整合 fegin 作为服务调用组件。
但是很多项目用不到微服务的,就是普通的单体应用,如何使用呢…

feign 说明

fegin发展简介

com.netflix.feign 从2016 年后闭源,由 io.github.openfeign 来维护。
在这里插入图片描述

feign是什么呢,

fegin其实就是一个http客户端调度框架。
底层 fegin默认情况下,使用的jdk 原生的 UrlConnections 发送 http请求,没有连接池,所以一般使用fegin的时候,对采用 HttpClient 或者 Okhttp 作为fegin 的客户端进行使用。

使用说明

参考官网:https://github.com/OpenFeign/feign

引入依赖

注:我这里没有引入 feign-core,而是直接引入 feign-jackson

        <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-jackson -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-jackson</artifactId>
            <version>11.2</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/io.github.openfeign/feign-okhttp -->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
            <version>11.2</version>
        </dependency>

创建 FeignClientFactory

这里只是简单配置测试使用,可进一步进行自定义封装。

public abstract class FeignClientFactory {
    /**
     * 创建 feign 客户端
     *
     * @param clazz class
     * @param url   url
     * @param <T>   泛型参数
     * @return T
     */
    public static <T> T create(Class<T> clazz, String url) {
        // 1、定义 okHttpClient
        okhttp3.OkHttpClient okHttpClient = new okhttp3.OkHttpClient()
                .newBuilder()
                .connectionPool(new ConnectionPool(5, 5L, TimeUnit.MINUTES))
                .connectTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS)
                .build();
                
        // 2、定义 feign 对应 OkHttpClient
        OkHttpClient client = new OkHttpClient(okHttpClient);

        // 3、创建 feign
        return Feign.builder()
                .client(client)
                .retryer(Retryer.NEVER_RETRY) // 重试机制
                .encoder(new FeignEncoder())  // 自定义:入参编码器
                .decoder(new StringDecoder()) // 定义:出参解码器
                .decode404() // 解码404,方便测试
                .target(clazz, url);
    }
}

配置自定义 encoder

为什么配置这个呢,可参考:https://cloud.tencent.com/developer/article/1588499
具体就是:
当方法参数没有标注@Param注解时,编码器会起作用。
eg:
如果方法参数并没有被模版使用,那么他会被收集放到一个Map里,然后交给Encoder处理。因为默认的 Encoder 是 StringDecoder,无法解析map就会报错了。
在这里插入图片描述

public class FeignEncoder implements Encoder {

    @Override
    public void encode(Object object, Type bodyType, RequestTemplate template) {
        if (object != null) {
            String jsonString = JSON.toJSONString(object);
            template.body(jsonString.getBytes(), StandardCharsets.UTF_8);
        }
    }
}

使用场景

这里我这里列举五种场景,基本可满足项目中用到场景。

1、表单传参 – 普通参数

1.1、定义api接口
public interface TestOpenFeignApi {

    /**
     * 表单传参 * V1
     * 参数必须要加 @Param注解,否则会报错,
     * 注意这里的 @Param 要区别于MyBatis @Param 注解
     *
     * @param id   id
     * @param code code
     */
    @RequestLine("POST openFeignApi/findByIdAndCode?id={id_}&code={code_}")
    String findByIdAndCode(@Param("id_") Integer id, @Param("code_") String code);
    }
1.2、对应SpringMvc接口
@RestController
@RequestMapping(value = "openFeignApi/")
public class OpenFeignController {


    /**
     * 表单传参------测试普通表单传参
     *
     * @param id   id
     * @param code code
     */
    @PostMapping("/findByIdAndCode")
    public Map findByIdAndCode(@RequestParam(required = false) Integer id, @RequestParam(required = false) String code) {
        HashMap<String, Object> hashMap = Maps.newHashMap();
        hashMap.put("id", id);
        hashMap.put("code", code);
        return hashMap;
    }
    }
1.3、Fegin调用
@SpringBootTest
public class OpenFeignTest {


    public static final String URL = "http://localhost:8080/";

    /**
     * 表单传参 -- 普通参数
     */
    @Test
    public void findById() {
        String str = FeignClientFactory.create(TestOpenFeignApi.class, URL).findByIdAndCode(1, "ali");
        System.out.println(str);
    }
    }

2、表单传参 – pojo

2.1、定义api接口

@QueryMap 作用等同于多个 @Param

    /**
     * 表单传参 (pojo) * V2
     * 负责将对象编码为Map查询参数名到值的映射。
     *
     * @param city pojo
     */
    @RequestLine("GET openFeignApi/findByCity")
    String findByCity(@QueryMap City city);
2.2、对应SpringMvc接口
    /**
     * 表单传参------测试pojo表单传参,注意这里不能带 @RequestParam 注解
     *
     * @param city city
     */
    @GetMapping("/findByCity")
    public String findByCity(City city) {
        System.out.println(city);
        return JSON.toJSONString(city);
    }
2.3、Fegin调用
    /**
     * 表单传参 -- pojo
     */
    @Test
    public void findByCity() {
        String str = FeignClientFactory.create(TestOpenFeignApi.class, URL)
                .findByCity(City.builder().cityCode(1001).cityName("杭州").build());
        System.out.println(str);
    }

3、表单传参 – map

3.1、定义api接口
    /**
     * 表单传参 (map) * V3
     * 负责将对象编码为Map查询参数名到值的映射。
     *
     * @param map map
     */
    @RequestLine("GET openFeignApi/findByMap")
    String findByMap(@QueryMap Map map);
3.2、对应SpringMvc接口
    /**
     * 表单传参------测试 map表单传参,注意这里一定要带 @RequestParam 注解,否则无效
     *
     * @param map map
     */
    @GetMapping("/findByMap")
    public Map findByMap(@RequestParam Map map) {
        System.out.println(map);
        return map;

    }
3.3、Fegin调用
    /**
     * 表单传参 -- map
     */
    @Test
    public void findByMap() {
        HashMap<Object, Object> map = Maps.newHashMap();
        map.put("cityCode", 1002);
        map.put("cityName", "上海");
        String str = FeignClientFactory.create(TestOpenFeignApi.class, URL)
                .findByMap(map);
        System.out.println(str);
    }

4、json传参 (带 header)

4.1、定义api接口
    /**
     * json 传参 (& header传参) * V2
     *
     * @param city (json格式)
     */
    @RequestLine("POST /openFeignApi/findByJson")
    @Headers({"Content-Type: application/json","Accept: application/json"})
    String findByJsonAndHeader(@HeaderMap Map<String, String> headerMap, City city) ;

4.2、对应SpringMvc接口
    /**
     * json传参
     *
     * @param city city
     */
    @PostMapping("/findByJson")
    public String findByJson(@RequestBody City city, HttpServletRequest request) {
        System.out.println(city);
        System.out.println(request.getHeader("token"));
        return JSON.toJSONString(city);

    }
4.3、Fegin调用
    /**
     * json传参(带 header) -- pojo
     */
    @Test
    public void findByJsonAndHeader() {
        Map<String, String> headerMap = Maps.newHashMap();
        headerMap.put("token", "123");
        headerMap.put("cookie", "456");
        City city = City.builder().cityCode(1001).cityName("杭州").build();

        String str = FeignClientFactory.create(TestOpenFeignApi.class, URL)
                .findByJsonAndHeader(headerMap, city);

        System.out.println(str);
    }

5、调用接口返回POJO对象(常用)

什么意思呢,就是调一个接口可以转换对应的 DTO,不用把返回接口的json字符串通过json解析工具解析为对象进行使用。

5.1 重定义 FeignClientFactory decoder
    public static <T> T create2(Class<T> clazz, String url) {
        return Feign.builder()
                .encoder(new FeignEncoder())
                // 使用JacksonDecoder做解码
                .decoder(new JacksonDecoder())
                .decode404()
                .target(clazz, url);
    }
5.2 定义api接口

这个接口是微信官方的,可以通过appid和secret及grantType直接调用获取token

public interface WechatSdkApi {
    @RequestLine("GET /cgi-bin/token?appid={appid}&secret={secret}&grant_type={grant_type}")
    WxToken getToken(@Param("appid") String appid,
                     @Param("secret") String secret,
                     @Param("grant_type") String grantType);
}
5.3 Fegin调用
@SpringBootTest
public class WeChatApi {
    @Autowired
    private SiteConfig siteConfig;

    /**
     * 根据 appid 和 secret 及 client_credential  获取 token
     */
    @Test
    public void getToken() {
        WechatSdkApi sdkApi = FeignClientFactory.create2(WechatSdkApi.class, ApiConstants.WECHAT_GET_OPENID);
        WxToken apiToken = sdkApi.getToken(siteConfig.getAppId(), siteConfig.getSecret(), siteConfig.getGrantType2());
        System.out.println(apiToken.getAccessToken());

    }
}
5.2 Fegin调用

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值