SpringCloud 学习笔记-OpenFeigin(五)
Mr.Lee 2021-05-07 17:16:23 SpringCloudOpenFeign
SpringCloud 学习笔记, 第五章 OpenFeign
在学习OpenFeign之前, 先看下整体的目录结构
- 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
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
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
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
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
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
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
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
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
是无感的...有种 任尔东西南北风, 我自岿然不动 的感觉了....