Striveonger

vuePress-theme-reco Mr.Lee    2015 - 2025
Striveonger Striveonger
主页
分类
  • 文章
  • 笔记
  • 工具
标签
时间轴
author-avatar

Mr.Lee

264

Article

134

Tag

主页
分类
  • 文章
  • 笔记
  • 工具
标签
时间轴

SpringCloud 学习笔记-OpenFeigin(五)

vuePress-theme-reco Mr.Lee    2015 - 2025

SpringCloud 学习笔记-OpenFeigin(五)

Mr.Lee 2021-05-07 17:16:23 SpringCloudOpenFeign

SpringCloud 学习笔记, 第五章 OpenFeign

在学习OpenFeign之前, 先看下整体的目录结构

image-20210507143835245

  • common-entity 用来放公用的实体(比如入参, 返回值)
  • common-service 用来存放 provider 暴露出的公共接口
  • provider-user 具体服务的提供方
  • consumer-user 服务的消费方

# common-service

# pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>common</artifactId>
        <groupId>com.striveonger.demo</groupId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>common-service</artifactId>

    <dependencies>
        <!-- 用于 provider 暴露的接口 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.striveonger.demo</groupId>
            <artifactId>common-entity</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# UserService

package com.striveonger.demo.common.service;

import com.striveonger.demo.common.entity.User;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author Mr.Lee
 * @description:
 * @date 2021-04-28 10:01 PM
 */
// 定义: 服务提供方, 都要加 provider 的前缀. 方便后期管理
@RequestMapping("provider/users") 
public interface UserService {

    /**
     * 用户列表
     *
     * @return
     */
    @GetMapping("")
    @ResponseBody
    List<User> users();

    /**
     * 根据ID, 获取用户
     * @param id
     * @return
     */
    @GetMapping(value = "/{id}")
    @ResponseBody
    User getUserById(@PathVariable("id") long id);

    /**
     * 添加用户
     * @param user
     * @return
     */
    @PostMapping(value = "", consumes = "application/json")
    Boolean addUser(@RequestBody User user);

    /**
     * 修改用户
     * @param user
     * @return
     */
    @PutMapping(value = "/{id}", consumes = "application/json")
    Boolean editUser(@PathVariable("id") long id, @RequestBody User user);

    /**
     * 删除用户
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    Boolean deleteUserById(@PathVariable("id") long id);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

这里需要强调的是:

@RequestBody

@PostMapping(value = "", consumes = "application/json")

OpenFeign 默认采用: Content-Type: application/json 的请求方式.

所以, 要在暴露的接口上, 显示的定义请求方式, 下面是OpenFeign发Post请求的样子

POST /provider/users HTTP/1.1
Host: 192.168.1.152:8811
User-Agent: Java/11.0.5
Content-Length: 59
Accept: application/json, application/*+json
Content-Type: application/json

{"id":0,"name":"B","password":"123","age":14,"degree":12.0}
1
2
3
4
5
6
7
8

# provider-user

# pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.striveonger.demo</groupId>
        <artifactId>spring-cloud-example</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>provider-user</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
        </dependency>
        <dependency>
            <groupId>com.striveonger.demo</groupId>
            <artifactId>common-entity</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>com.striveonger.demo</groupId>
            <artifactId>common-service</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# UserController

package com.striveonger.demo.serive.user.web.controller;

import cn.hutool.core.bean.BeanUtil;
import com.google.common.collect.Lists;
import com.striveonger.demo.common.entity.User;
import com.striveonger.demo.common.service.UserService;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Objects;

/**
 * @author Mr.Lee
 * @description: 具体的服务实现
 * @date 2021-03-31 4:31 PM
 */
@RestController
public class UserController implements UserService {

    private List<User> users = Lists.newLinkedList();

    public UserController() {
        users.add(new User(1, "tom", "123", 5, 2.5));
        users.add(new User(2, "jerry", "123", 3, 2.2));
    }

    @Override
    public List<User> users() {
        return users;
    }

    @Override
    public User getUserById(long id) {
        User user = users.stream().filter(u -> u.getId() == id).findAny().orElse(null);
        return user;
    }

    @Override
    public Boolean addUser(User user) {
        user.setId(users.size() + 1);
        users.add(user);
        return true;
    }

    @Override
    public Boolean editUser(long id, User user) {
        if (Objects.isNull(user) || user.getId() <= 0) {
            throw new RuntimeException("用户信息不正确...");
        }
        User exist = users.stream().filter(u -> u.getId() == user.getId()).findAny().orElse(null);
        if (Objects.nonNull(exist)) {
            BeanUtil.copyProperties(user, exist);
            return true;
        }
        return false;
    }


    @Override
    public Boolean deleteUserById(long id) {
        User exist = users.stream().filter(u -> u.getId() == id).findAny().orElse(null);
        if (Objects.nonNull(exist)) {
            users.remove(exist);
            return true;
        }
        return false;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

就是一个普通的服务提供方, 不同的是他实现了, 暴露在外的接口.

这里注意: 实现接口的方法时, 方法会复用接口上的注解哦~

# consumer-user

# pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.striveonger.demo</groupId>
        <artifactId>spring-cloud-example</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>consumer-user</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.striveonger.demo</groupId>
            <artifactId>common-entity</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>com.striveonger.demo</groupId>
            <artifactId>common-service</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
      	<!-- 在客户端引入OpenFeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>

    </dependencies>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

# UserConsumerService

package com.striveonger.demo.client.user.service;

import com.striveonger.demo.common.constants.ProviderServiceConstants;
import com.striveonger.demo.common.service.UserService;
import org.springframework.cloud.openfeign.FeignClient;

/**
 * @author Mr.Lee
 * @description:
 * @date 2021-05-04 9:50 PM
 */
@FeignClient(name = ProviderServiceConstants.PROVIDER_USER)
public interface UserConsumerService extends UserService {
		// 这里一定要是 interface 下面有解释哦~
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# UserConsumerApplication

package com.striveonger.demo.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;


/**
 * @author Mr.Lee
 * @description:
 * @date 2021-03-31 10:28 PM
 */
@SpringBootApplication
@EnableFeignClients
public class UserConsumerApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserConsumerApplication.class, args);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

@FeignClient 的描述:

/**
 * 对接口的注释, 声明一个REST客户端应该具有该接口
 * 创建(例如, 用于自动装配到另一个组件). 如果Ribbon是可用的, 它将是用于后端请求的负载均衡, 负载均衡器可以配置
 */

/**
 * 这里是原文
 * Annotation for interfaces declaring that a REST client with that interface should be
 * created (e.g. for autowiring into another component). If ribbon is available it will be
 * used to load balance the backend requests, and the load balancer can be configured
 * using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
 *
 * @author Spencer Gibb
 * @author Venil Noronha
 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

注意哈~ 是对接口的描述

@EnableFeignClients 开启扫描Feign客户端

/**
 * 扫描用@FeignClient描述的接口. 配置组件扫描指令以与@Configuration类一起使用
 */

/**
 * 这里是原文
 * Scans for interfaces that declare they are feign clients (via
 * {@link org.springframework.cloud.openfeign.FeignClient} <code>@FeignClient</code>).
 * Configures component scanning directives for use with
 * {@link org.springframework.context.annotation.Configuration}
 * <code>@Configuration</code> classes.
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @since 1.0
 */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# UserController

package com.striveonger.demo.client.user.web.controller;

import com.striveonger.demo.client.user.service.UserConsumerService;
import com.striveonger.demo.common.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Optional;

/**
 * @author Mr.Lee
 * @description:
 * @date 2021-03-31 5:47 PM
 */
@RestController
@RequestMapping("users")
public class UserController {


    @Autowired
    RestTemplate rest;

    @Autowired
    UserConsumerService userService;

    /**
     * @return
     */
    @GetMapping("")
    public List<User> users() {
        String url = "http://provider-user/provider/users";

//        RestTemplate rest = new RestTemplate();
//        rest.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        // 没有泛型的写法
        return rest.getForEntity(url, List.class).getBody();

//        ParameterizedTypeReference<List<User>> type = new ParameterizedTypeReference<List<User>>() {};
//        ResponseEntity<List<User>> response = rest.exchange(url, HttpMethod.GET, null, type);
//        return response.getBody();
    }

    @GetMapping("/{id}")
    public User getUserById(@PathVariable("id") Long id) {
        User user = userService.getUserById(id);
        return user;
    }

    @PostMapping("")
    public boolean addUser(User user) {
        String url = "http://provider-user/provider/users";
        ResponseEntity<Boolean> response = rest.postForEntity(url, user, Boolean.class);
        // 这里因为使用的是APPLICATION_FORM_URLENCODED的方式, 所以需要指定一下.
//        ResponseEntity<Boolean> response = rest.postForEntity(url, user, Boolean.class);
        return Optional.of(response).map(ResponseEntity::getBody).orElse(false);
    }

    @PutMapping("")
    public boolean editUser(User user) {
        return userService.editUser(user.getId(), user);
    }

    @DeleteMapping("/{id}")
    public boolean deleteUserById(@PathVariable("id") int id) {
        return userService.deleteUserById(id);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

这里有坑哈~

如果Consumer(消费方)中的@RequestMapping 有跟Common-Service(provider 所暴露的接口) 有相同的地址的情况下, 会出现:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'requestMappingHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Invocation of init method failed; 
nested exception is java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'com.striveonger.demo.client.user.service.UserConsumerService' method
1
2

重点是: Ambiguous mapping.: 模糊的映射....

因为Spring在加载时, 也会扫到引用的Common-Service中接口中的 @RequestMapping

# 总结

OpenFeign 是用于改变Consumer(消费方)调用Provider(服务方)的方式.

之前我们要调用Provider 要用原始的方法发一个http请求, 来完成调用. 但是在使用了OpenFeign后, 就会想接口调用一样简单了

这一切的改变, 对于Provider 是无感的...有种 任尔东西南北风, 我自岿然不动 的感觉了....