github:https://github.com/Maple521/springcloud/tree/master/feign-consumer-%E5%8F%82%E6%95%B0
github:https://github.com/Maple521/springcloud/tree/master/eureka-producer-%E5%8F%82%E6%95%B0
在上一节的示例中,我们使用Spring Cloud Feign实现的是一个不带参数的REST服务绑定。然而显示系统中的各种业务接口要比它复杂得多,我们会在HTTP的各个位置传入各种不同类型的参数,并且在返回请求响应的时候也可能是一个复杂的对象结构。在本节中,我们将详细介绍Feign中对几种不同仅是参数的绑定方法。
在开始介绍Spring Cloud Feign的参数绑定之前,我们先扩展一下服务提供方hello-service。增加下面这些接口定义,其中包含带有Request参数的请求、带有Header信息的请求、带有RequestBody的请求以及请求响应体中是一个对象的请求。
package com.maple.eurekaproducer.controller;
import com.maple.eurekaproducer.model.User;
import org.apache.log4j.Logger;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.Random;
@RestController
public class HelloController {
private final Logger logger = Logger.getLogger(getClass());
@Resource
private DiscoveryClient client;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String index() throws Exception {
ServiceInstance instance = client.getLocalServiceInstance();
//让处理线程等待几秒钟
int sleepTime = new Random().nextInt(3000);
logger.info("sleepTime:" + sleepTime);
Thread.sleep(sleepTime);
logger.info("/hello,host:" + instance.getHost() + ",service_id:" + instance.getServiceId());
return "Hello World";
}
@RequestMapping(value = "/hello1", method = RequestMethod.GET)
public String hello(@RequestParam String name) {
return "Hello " + name;
}
@RequestMapping(value = "/hello2", method = RequestMethod.GET)
public User hello(@RequestHeader String name, @RequestHeader Integer age) {
return new User(name, age);
}
@RequestMapping(value = "/hello3", method = RequestMethod.POST)
public String hello(@RequestBody User user) {
return "Hello " + user.getName() + "," + user.getAge();
}
}
User对象的定义如下,需要注意的,这里必须要有User默认构造函数。不然,Spring Cloud Feign根据JSON字符串转换User对象时会抛出异常。
package com.maple.eurekaproducer.model;
public class User {
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "name=" + name + ",age=" + age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
在完成了对hello-service的改造之后,下面我们开始在快速入门示例的feign-consumer应用中实现这些新增的请求的绑定。
(1)首先,在feign-consumer中创建与上面一样的User类
(2)然后,在HelloService接口中增加对上述三个新增接口的绑定声明,修改后,完成的HelloService接口如下所示:
package com.feign.feignconsumer.service;
import com.feign.feignconsumer.model.User;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.*;
@FeignClient("hello-service")
public interface HelloService {
@RequestMapping("/hello")
String hello();
@RequestMapping(value = "/hello1", method = RequestMethod.GET)
String hello(@RequestParam("name") String name);
@RequestMapping(value = "/hello2", method = RequestMethod.GET)
public User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age);
@RequestMapping(value = "/hello3", method = RequestMethod.POST)
public String hello(@RequestBody User user);
}
这里一定要注意,在定义各参数绑定时, @RequestParam、@RequestHeader等可以指定参数名称的注解,它们的value千万不能少。在Spring MVC程序中,这些注解会根据参数名来作为默认值,但是在Feign中绑定的参数必须通过value属性来指明具体的参数名,不然会抛出IllegalStateException异常,value属性不能为空。
Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0
(3)最后,在ConsumerController中新增一个/feign-consumer2接口,来对本节新增的声明接口进行调用,修改后的完整代码如下:
package com.feign.feignconsumer.controller;
import com.feign.feignconsumer.model.User;
import com.feign.feignconsumer.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ConsumerController {
@Autowired
HelloService helloService;
@RequestMapping(value = "/feign-consumer", method = RequestMethod.GET)
public String helloConsumer() {
return helloService.hello();
}
@RequestMapping(value = "/feign-consumer2", method = RequestMethod.GET)
public String helloConsumer2() {
StringBuilder sb = new StringBuilder();
sb.append(helloService.hello()).append("\n");
sb.append(helloService.hello("maple")).append("\n");
sb.append(helloService.hello("maple", 18)).append("\n");
sb.append(helloService.hello(new User("maple", 18))).append("\n");
return sb.toString();
}
}
测试验证
在完成上述改造之后,启动服务注册中心、两个hello-service服务以及我们改造过的feign-consumer。启动完成,如Eureka-server信息面板所示:
通过发送GET请求导http://localhost:9001/feign-consumer2,触发HelloService对新增接口的调用。最终,我们会获得如下输出,代表接口绑定和调用成功。