声明式服务调用:Spring Cloud Feign —— 参数绑定

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信息面板所示:

声明式服务调用:Spring Cloud Feign —— 参数绑定_第1张图片

通过发送GET请求导http://localhost:9001/feign-consumer2,触发HelloService对新增接口的调用。最终,我们会获得如下输出,代表接口绑定和调用成功。

声明式服务调用:Spring Cloud Feign —— 参数绑定_第2张图片

你可能感兴趣的:(Spring,Cloud)