Java 开源项目 OpenFeign —— feign 结合 SpringBoot

Posted 四维胖次

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 开源项目 OpenFeign —— feign 结合 SpringBoot相关的知识,希望对你有一定的参考价值。

1. 前言

Spring 对 Feign 做了封装,包括常用的 encoder/decoder ,让我们能用 Bean 的形式使用 Feign。我们将沿用之前的代码。

1.1 Maven 依赖

 1         <dependency>
 2             <groupId>org.springframework.cloud</groupId>
 3             <artifactId>spring-cloud-starter-openfeign</artifactId>
 4         </dependency>
 5         <dependency>
 6             <groupId>org.springframework.cloud</groupId>
 7             <artifactId>spring-cloud-starter</artifactId>
 8         </dependency>
 9 
10     <dependencyManagement>
11         <dependencies>
12             <dependency>
13                 <groupId>org.springframework.cloud</groupId>
14                 <artifactId>spring-cloud-dependencies</artifactId>
15                 <version>Hoxton.SR3</version>
16                 <type>pom</type>
17                 <scope>import</scope>
18             </dependency>
19         </dependencies>
20     </dependencyManagement>

 

1.2 激活 openfeign

在 Springboot 启动类加上注解  @EnableFeignClients

2. SpringMvc 注解

与原生的 Feign 不同,在 Springboot 中要使用到 SpringMvc 的注解,这存在一些限制(比如动态改变 URL ),需要我们找 workRound 

2. 编写代码

由于使用 SpringMVC 风格,编写的代码与 feign 原生的有不少出入,需要仔细对比。

2.1 改写官方教程

我们只需要创建与 GitHub 接口相似的代码,与响应的测试代码

2.1.1 创建 Client

 1 /**
 2  * @author pancc
 3  * @version 1.0
 4  */
 5 @FeignClient(name = "gitHubs", url = " https://api.github.com", qualifier = "gitHubsClient")
 6 public interface GitHubClient {
 7     @GetMapping(value = "/repos/{owner}/{repo}/contributors")
 8     @ResponseBody
 9     List<SimpleGit.Contributor> contributors(@PathVariable("owner") String owner, @PathVariable("repo") String repo);
10 
11     @Data
12     @JsonIgnoreProperties(ignoreUnknown = true)
13     class Contributor {
14         String login;
15         int contributions;
16     }
17 
18 }

 

2.1.2 创建测试代码

 1 @SpringBootTest
 2 class GitHubClientTest {
 3     @Autowired
 4     GitHubClient gitHubClient;
 5 
 6     @Test
 7     void contributors() {
 8         List<SimpleGit.Contributor> contributors = gitHubClient.contributors("OpenFeign", "feign");
 9         Assertions.assertNotNull(contributors);
10         Assertions.assertNotEquals(0, contributors.size());
11 
12     }
13 }

 

2.1.3 动态改变请求 url

从上边的代码可以看到,我们将 url 写死在了 GitHubClient 中。让我们新增一个方法,增加 URI 类型的参数

1     @GetMapping(value = "/repos/{owner}/{repo}/contributors")
2     @ResponseBody
3     List<SimpleGit.Contributor> contributorsWithURL(URI uri, @PathVariable("owner") String owner, @PathVariable("repo") String repo);

 

然后用任意字符串替换 FeignClient#url 并写一个测试:

1     @Test
2     void contributorsWithURL() {
3         List<SimpleGit.Contributor> contributors =    gitHubClient.contributorsWithURL(URI.create("https://api.github.com"), "OpenFeign", "feign");
4         Assertions.assertNotNull(contributors);
5         Assertions.assertNotEquals(0, contributors.size());
6     }

 

2.2 改写 DictFeign

2.2.1 编写 DictClient 接口

 1 /**
 2  * @author pancc
 3  * @version 1.0
 4  */
 5 @FeignClient(
 6         name = "dictClients",
 7         url = "127.0.0.1:8080",
 8         path = "dic",
 9         qualifier = "dictClients"
10 )
11 public interface DictClient {
12     /**
13      * @see FeignController#details
14      */
15     @GetMapping("details")
16     Dict details();
17 
18     /**
19      * @see FeignController#details
20      */
21     @GetMapping("details")
22     Dict detailsWithURI(URI uri);
23 
24 
25     /**
26      * @see FeignController#startsWith
27      */
28     @GetMapping("startsWith/{query}")
29     List<String> startsWith(@PathVariable("query") String query);
30 
31 
32     /**
33      * @see FeignController#updateFirst
34      */
35     @PutMapping("updateFirst?target={target}")
36     List<String> updateFirst(@PathVariable("target") String target);
37 
38     /**
39      * @see FeignController#headers
40      */
41     @GetMapping("headers")
42     Map<String, Object> headers(@RequestHeader Map<String, Object> headers);
43 
44     /**
45      * @see FeignController#startAndEnd
46      */
47     @GetMapping("query")
48     List<String> startAndEnd(@RequestParam Map<String, String> map);
49 
50     /**
51      * @see FeignController#replace
52      */
53     @PutMapping(value = "replace", consumes = MediaType.APPLICATION_JSON_VALUE)
54     List<String> replace(Map<String, String> map);
55 
56 
57     /**
58      * @see FeignController#add
59      */
60     @PostMapping(value = "add", consumes = MediaType.APPLICATION_JSON_VALUE)
61     List<String> add(Map<String, Object> map);
62 
63     /**
64      * @see FeignController#deleteFirst
65      */
66     @DeleteMapping("deleteFirst")
67     List<String> deleteFirst();
68 }

 

2.2.2 编写对应的测试方法

 1 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
 2 class DictClientTest {
 3     private final Dict dict = Dict.Instance;
 4     @Autowired
 5     private DictClient dictClient;
 6 
 7 
 8     @Test
 9     void details() {
10         Assertions.assertEquals(dict, dictClient.details());
11     }
12 
13     @Test
14     void startsWith() {
15         Assertions.assertEquals(3, dictClient.startsWith("a").size());
16     }
17 
18 
19     @Test
20     void startAndEnd() {
21         Map<String, String> map = Maps.newHashMap();
22         map.put("startsWith", "e");
23         map.put("endsWith", "e");
24         Assertions.assertEquals(1, dictClient.startAndEnd(map).size());
25     }
26 
27     @Test
28     void replace() {
29         Assertions.assertNotEquals(dictClient.replace(Collections.singletonMap("bike", "bikes")).indexOf("bikes"), -1);
30     }
31 
32     @Test
33     void updateFirst() {
34         Assertions.assertEquals("game", dictClient.updateFirst("game").get(0));
35     }
36 
37     @Test
38     void deleteFirst() {
39         Assertions.assertEquals(13, dictClient.deleteFirst().size());
40     }
41 
42 
43     @Test
44     void headers() {
45         Map<String, Object> headers = Maps.newHashMap();
46         headers.put("age", 15);
47         headers.put("length", 21);
48         Assertions.assertTrue(dictClient.headers(headers).containsKey("age"));
49         Assertions.assertTrue(dictClient.headers(headers).containsKey("length"));
50     }
51 
52     @Test
53     void add() {
54         String var1 = "go~";
55         String var2 = "back";
56         List<String> adds = dictClient.add(Maps.toMap(Arrays.asList(var1, var2), input -> input));
57         Assertions.assertTrue(adds.contains(var1));
58         Assertions.assertTrue(adds.contains(var2));
59 
60     }
61 }

 

2.3 改写登录流程

2.3.1 配置 FormEncoder

由于使用 Spring 默认使用 Jackson(Json) ,我们需要手动配置一个 FormEncoder,需要注意的是配置类不能写成 Component 以避免自动配置的 encoder 被替换;

 1 /**
 2  * @author pancc
 3  * @version 1.0
 4  */
 5 public class LoginClientConfig {
 6     @Bean
 7     public FormEncoder formEncoder() {
 8         return new FormEncoder(new JacksonEncoder());
 9     }
10 }

 

2.3.2 编写 LoginClient

 1 /**
 2  * @author pancc
 3  * @version 1.0
 4  */
 5 @FeignClient(name = "loginClient", url = "127.0.0.1:8080", configuration = LoginClientConfig.class)
 6 public interface LoginClient {
 7     /**
 8      * @see LoginController#loginWithJson
 9      */
10     @PostMapping(value = "login", consumes = MediaType.APPLICATION_JSON_VALUE)
11     Response loginWithJson(@RequestBody UserInfo userInfo);
12 
13     /**
14      * @see LoginController#loginWithForm
15      */
16     @PostMapping(value = "login", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
17     Response loginWithForm(UserInfo userInfo);
18 
19 
20     /**
21      * @see LoginController#loginWithToken
22      */
23     @PostMapping("login")
24     Response loginWithToken(@RequestHeader("Authorization") String token);
25 
26 
27 }

 

2.3.3 编写测试

 1 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
 2 class LoginClientTest {
 3 
 4     @Autowired
 5     private LoginClient loginClient;
 6 
 7     @Test
 8     void loginWithJson() {
 9         Response res = loginClient.loginWithJson(new UserInfo().setPassword("root").setUsername("admin"));
10         Assertions.assertEquals(200, res.getStatus());
11     }
12 
13     @Test
14     void loginWithForm() {
15         Response res = loginClient.loginWithForm(new UserInfo().setPassword("root").setUsername("admin"));
16         Assertions.assertEquals(200, res.getStatus());
17     }
18 
19     @Test
20     void loginWithToken() {
21         Response res = loginClient.loginWithToken("Bearer user-token");
22         Assertions.assertEquals(200, res.getStatus());
23     }
24 
25 }

 

3. 代理

代理的使用总是不可避免地,就像怕配置 encoder 一样,让我们为 GitHubClient 配置一个代理

3.1 代理配置类

参照 feign 的基本使用与 2.3.1,我们可以很快的写配置类,同样避免将配置类写成 Component

 1 /**
 2  * @author pancc
 3  * @version 1.0
 4  */
 5 public class GitHubClientConfig {
 6     @Bean
 7     public Client client() {
 8         okhttp3.OkHttpClient okHttpClient =
 9                 new okhttp3.OkHttpClient.Builder().proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 10808))).build();
10         return new OkHttpClient(okHttpClient);
11     }
12 }

 

3.2 修改 GithubClient 配置

1 @FeignClient(name = "gitHubs", url = "https://api.github.com", qualifier = "gitHubsClient", configuration = GitHubClientConfig.class)
2 public interface GitHubClient {
3 ....

 

4. 总结要点

4.1 依赖

     <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

 

4.2 编写方式

  1. 启动注解 @EnableFeignClients
  2. feign 接口使用 @FeignClient 注解
  3. feign 接口使用 SpringMVC 风格
  4. feign 的配置写在一个不被 Spring 实例化的配置类中,各种 feign 基本模块写成 Bean。启用时只添加配置类进相应的 feign 接口的注解字段中
  5. 使表单需要配置 FormEncoder
  6. feign 接口方法的第一个参数可以为 URI ,用于动态替换 FeignClient 中的 url 

 

 

 

 

 

以上是关于Java 开源项目 OpenFeign —— feign 结合 SpringBoot的主要内容,如果未能解决你的问题,请参考以下文章

PassJava 开源 : 整合 OpenFeign实现声明式远程调用 #私藏项目实操分享#

springcloud中openfeign止hystrix的几个问题

springcloud中openfeign止hystrix的几个问题

springcloud中openfeign止hystrix的几个问题

springcloud中openfeign止hystrix的几个问题

springcloud中openfeign止hystrix的几个问题