03.`feign`→服务间的通信
1. 依赖
-
spring-cloud-starter-openfeign
依赖<dependencies> <!-- feign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
-
说明
-
消费者
添加spring-cloud-starter-openfeign
的依赖即可,提供者不需要
-
spring-cloud-starter-openfeign
的父依赖<properties> <spring-cloud.version>Hoxton.SR7</spring-cloud.version> </properties> <dependencyManagement> <dependencies> <!-- spring-cloud 父依赖 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
-
提供者
要注册
到注册中心,消费者
要从注册中心拉取
提供者信息<properties> <spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version> </properties> <dependencies> <!-- nacos 注册中心 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> </dependencies> <dependencyManagement> <!-- spring-cloud-alibaba 父依赖 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencyManagement>
-
2. 配置
-
feign-provider
提供者-
application.yml
主配置文件server: port: 8081 servlet: # 提供者一般不设置前缀,这是为了演示 context-path: /api/v1 spring: application: # 注册到注册中心的服务名 name: feign-provider cloud: nacos: discovery: # nacos注册中心的地址 server-addr: 120.25.207.44:8848 cluster-name: ${spring.application.name}
-
启动类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; // 开启 nacos 注册中心扫描,发现该服务 @EnableDiscoveryClient @SpringBootApplication public class FeignProviderApplication { public static void main(String[] args) { SpringApplication.run(FeignProviderApplication.class, args); } }
-
-
feign-consumer
消费者-
application.yml
主配置文件server: port: 8082 spring: application: # 注册到注册中心的服务名,不注册到注册中心可以不写 name: feign-consumer cloud: nacos: discovery: # nacos 注册中心的地址 server-addr: 120.25.207.44:8848 # 不将自己注册到注册中心 register-enabled: false feign: okhttp: # feign 启用 okhttp,性能最好 enabled: true
-
启动类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.openfeign.EnableFeignClients; // 启动 feign 客户端 @EnableFeignClients // 开启 nacos 注册中心扫描,发现该服务 @EnableDiscoveryClient @SpringBootApplication public class FeignConsumerApplication { public static void main(String[] args) { SpringApplication.run(FeignConsumerApplication.class, args); } }
-
@EnableFeignClients
启动feign
客户端 -
@EnableDiscoveryClient
开启注册中心扫描,发现该服务
-
-
3. feign
组件的使用
-
feign-consumer
消费者的远程调用类
中用@FeignClient
注解标记import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { }
-
提供者的服务名value = "feign-provider"
的值feign-provider
为提供者
注册到注册中心的服务名
-
path = "/api/v1/provider"
是提供者的ProviderController
的访问路径,/api/v1/
是提供者在application.yml
主配置文件配置的context-path
-
-
消费者
的远程调用类
中,一个参数
必须使用@RequestParam
或@PathVariable
修饰,feign
底层一个参数
时会自动放进请求体
中默认使用post
请求(不用@RequestParam
或@PathVariable
有可能取到的值为null
),Get
请求直接报错,提供者的controller
可以不用@RequestParam
修饰-
@RequestParam
栗子,GetMapping
或者PostMapping
都可以-
提供者
controller
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @GetMapping("/testParam") public String testParam(String userId) { return "ProviderController_testParam_" + userId; } @PostMapping("/testPostMappingParam") public String testPostMappingParam(String userId) { return "ProviderController_testPostMappingParam_" + userId; } }
-
消费者
-
远程调用类
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { /** * 对应 public String testParam(String userId){} * * @param userId 用户id * @return 字符串 */ @GetMapping("/testParam") String testParam(@RequestParam String userId); /** * 对应 public String testPostMappingParam(String userId){} * * @param userId 用户id * @return 字符串 */ @PostMapping("/testPostMappingParam") String testPostMappingParam(@RequestParam String userId); }
-
controller
import com.sheng.cloud.feign.service.ProviderService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class ConsumerController { @Resource private ProviderService providerService; @GetMapping("/testParam") public String testParam(String userId) { return providerService.testParam(userId); } @PostMapping("/testPostMappingParam") public String testPostMappingParam(String userId) { return providerService.testPostMappingParam(userId); } }
-
-
-
@PathVariable
栗子,GetMapping
或者PostMapping
都可以-
提供者
controller
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @GetMapping("/testPathVariable/{userId}") public String testPathVariable(@PathVariable String userId) { return "ProviderController_testPathVariable_" + userId; } }
-
消费者
-
远程调用类
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { /** * 对应 public String testPathVariable(@PathVariable String userId){} * * @param userId 用户id * @return 字符串 */ @GetMapping("/testPathVariable/{userId}") String testPathVariable(@PathVariable String userId); }
-
controller
import com.sheng.cloud.feign.service.ProviderService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class ConsumerController { @Resource private ProviderService providerService; @GetMapping("/testPathVariable/{userId}") public String testPathVariable(@PathVariable String userId) { return providerService.testPathVariable(userId); } }
-
-
-
-
消费者
的远程调用类
中,两个参数或以上
必须使用@RequestParam
或@PathVariable
修饰,不修饰不能启动,会直接报错。提供者的controller
可以不用@RequestParam
修饰-
@RequestParam
栗子,GetMapping
或者PostMapping
都可以-
提供者
controller
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @PostMapping("/login") public String login(String username, String password) { return "ProviderController_login_username: " + username + "_password: " + password; } }
-
消费者
-
远程调用类
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { /** * 对应 public String login(String username, String password){} * * @param username 用户名 * @param password 密码 * @return 字符串 */ @PostMapping("/login") String login(@RequestParam String username, @RequestParam String password); }
-
controller
import com.sheng.cloud.feign.service.ProviderService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class ConsumerController { @Resource private ProviderService providerService; @PostMapping("/login") public String login(String username, String password) { return providerService.login(username, password); } }
-
-
-
@PathVariable
栗子,GetMapping
或者PostMapping
都可以-
提供者
controller
import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @GetMapping("/loginByPathVariable/{username}/{password}") public String loginByPathVariable(@PathVariable String username, @PathVariable String password) { return "ProviderController_login_username: " + username + "password: " + password; } }
-
消费者
-
远程调用类
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { /** * 对应 public String loginByPathVariable(@PathVariable String username, @PathVariable String password){} * * @param username 用户名 * @param password 密码 * @return 字符串 */ @GetMapping("/loginByPathVariable/{username}/{password}") String loginByPathVariable(@PathVariable String username, @PathVariable String password); }
-
controller
import com.sheng.cloud.feign.service.ProviderService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class ConsumerController { @Resource private ProviderService providerService; @GetMapping("/loginByPathVariable/{username}/{password}") public String loginByPathVariable(@PathVariable String username, @PathVariable String password) { return providerService.loginByPathVariable(username, password); } }
-
-
-
-
如果
消费者
的远程调用类
的形参是JavaBean
对象,那么提供者的controller
对应的方法的形参
必须用@RequestBody
标记,否则取到的值为null
,因为feign
底层默认将对象序列化成json
数据进行传递。-
结果
-
不用
不用@RequestBody修饰@RequestBody
修饰 -
@RequestBody修饰@RequestBody
修饰
-
-
栗子
-
提供者
controller
import com.sheng.cloud.feign.dto.UserDto; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/provider") public class ProviderController { @PostMapping("/getUser") public UserDto getUser(@RequestBody UserDto userDto) { return userDto; } }
-
消费者
-
远程调用类
import com.sheng.cloud.feign.dto.UserDto; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*; @Service @FeignClient(value = "feign-provider", path = "/api/v1/provider") public interface ProviderService { /** * 对应 public UserDto getUser(@RequestBody UserDto userDto){} * * @param userDto 对象 * @return UserDto */ @PostMapping("/getUser") UserDto getUser(UserDto userDto); }
-
controller
import com.sheng.cloud.feign.dto.UserDto; import com.sheng.cloud.feign.service.ProviderService; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; @RestController @RequestMapping("/consumer") public class ConsumerController { @Resource private ProviderService providerService; @PostMapping("/getUser") public UserDto getUser(@RequestBody UserDto userDto) { return providerService.getUser(userDto); } }
-
-
-