zoukankan      html  css  js  c++  java
  • SpringCloud开发学习总结(七)—— 声明式服务调用Feign(二)

    参数绑定 

      在上一章的示例中,我们使用Spring Cloud Feign实现的是一个不带参数的REST服务绑定。然而现实系统中的各种业务接口要比它复杂得多,我们有时会在HTTP的各个位置传入各种不同类型的参数,并且在返回请求响应的时候也可能是一个复杂的对象结构。在这章中,我们将详细介绍Feign中对几种不同形式参数的绑定方法。

      在介绍Spring Cloud Feign的参数绑定之前,先扩展服务提供方hello-service。增加包含带有Request参数的请求、带有Header信息的请求、带有RequestBody的请求以及请求响应体中是一个对象的请求。

     1 @RestController
     2 public class HelloController {
     3     @RequestMapping("/hello")
     4     public String hello() {
     5         return "hello provide";
     6     }
     7     
     8     @RequestMapping("/hello1")
     9     public String hello(@RequestParam String name) {
    10         return "Hello "+name;
    11     }
    12     
    13     @RequestMapping("/hello2")
    14     public User hello(@RequestHeader String name,@RequestHeader Integer age) {
    15         return new User(name,age);
    16     }
    17     
    18     @RequestMapping("/hello3")
    19     public String hello(@RequestBody User user) {
    20         return "Hello "+user.getName()+","+user.getAge();
    21     }
    22 }
     1 public class User {
     2     private String name;
     3     private Integer age;
     4     public User() {
     5     }
     6     public User(String name, Integer age) {
     7         this.name = name;
     8         this.age = age;
     9     }
    10     public String getName() {
    11         return name;
    12     }
    13     public void setName(String name) {
    14         this.name = name;
    15     }
    16     public Integer getAge() {
    17         return age;
    18     }
    19     public void setAge(Integer age) {
    20         this.age = age;
    21     }
    22     @Override
    23     public String toString() {
    24         return "name=" + name + ", age=" + age;
    25     }
    26 }

      在完成了对hello-service的改造之后,下面在feign-consumer应用中实现这些新增的请求的绑定。

    • 首先,在feign-consumer中创建与上面一样的User类。
    • 然后,在HelloService接口中增加对上述三个新增接口的绑定声明,修改后的HelloService接口如下所示:
     1 @FeignClient("hello-service")  //用于通知Feign组件对该接口进行代理(不需要编写接口实现),name属性指定我们要调用哪个服务。使用者可直接通过@Autowired注入。
                         //原理:Spring Cloud应用在启动时,Feign会扫描标有@FeignClient注解的接口,生成代理,并注册到Spring容器中。生成代理时Feign会为每个接口方法创建一个RequetTemplate对象,
                         //该对象封装了HTTP请求需要的全部信息,请求参数名、请求方法等信息都是在这个过程中确定的,Feign的模板化就体现在这里。
    2 public interface HelloService { 3 4 @RequestMapping(value="/hello") 5 String hello(); 6 @RequestMapping(value="/hello1",method=RequestMethod.GET) 7 String hello(@RequestParam("name")String name); 8 @RequestMapping(value="/hello2",method=RequestMethod.GET) 9 String hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age); 10 @RequestMapping(value="/hello3",method=RequestMethod.POST) 11 String hello(@RequestBody User user); 12 13 }
    • 最后,在ConsumerController中新增一个/feign-consumer2接口,来对本节新增的声明接口进行调用:
     1 @RestController
     2 public class ConsumeController {
     3     
     4     @Autowired
     5     HelloService helloService;
     6     @Autowired
     7     RefactorHelloService refactorHelloService;
     8     
     9     @RequestMapping(value="/feign-consumer",method=RequestMethod.GET)
    10     public String helloConsumer(){
    11         return helloService.hello();
    12     }
    13     
    14     @RequestMapping(value="/feign-consumer2",method=RequestMethod.GET)
    15     public String helloConsumer2(){
    16         StringBuilder sb = new StringBuilder();
    17         sb.append(helloService.hello()).append("
    ");
    18         sb.append(helloService.hello("LULU")).append("
    ");
    19         sb.append(helloService.hello("LULU",18)).append("
    ");
    20         sb.append(helloService.hello(new User("LULU",18))).append("
    ");
    21         return sb.toString();
    22     }
    23     
    24 }

    继承特性

      通过上面的示例,可以发现当时在消费方用SpringMVC的注解来绑定服务接口时,可以几乎完全从服务提供方的Controller中依靠复制操作,构建出相应的服务客户端绑定接口。既然存在那么多可复制的操作,自然需要考虑这部分内容是否可以得到进一步的抽象?在Spring Cloud Feign中,针对该问题提供了继承特性来帮助解决这些可复制的操作,进一步减少编码量。下面,详细介绍如何通过Spring Cloud Feign的继承特性来实现REST接口定义的复用。

    • 为了能够复用DTO与接口定义,首先创建一个基础的Maven工程,命名为hello-service-api
    • 由于在hello-service-api中需要定义可同时服用于服务端与客户端的接口,需要用到SpringMVC注解,所以在pom.xml中引入spring-boot-starter-web依赖
    • 将上一节中实现的User对象复制到该工程,并创建HelloService接口,该接口的User为本项目的User
     1 @RequestMapping(value="/refactor")
     2 public interface HelloService {
     3     
     4     @RequestMapping(value="/hello4",method=RequestMethod.GET)
     5     String hello(@RequestParam("name") String name);
     6     @RequestMapping(value="/hello5",method=RequestMethod.GET)
     7     User hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age);
     8     @RequestMapping(value="/hello6",method=RequestMethod.POST)
     9     String hello(@RequestBody User user);
    10     
    11 }
    • 下面对服务提供者hello-service进行重构,在pom.xml中新增对hello-service-api的依赖
            <dependency>
                <groupId>com.kingbrook</groupId>
                <artifactId>hello-service-api</artifactId>
                <version>0.0.1-SNAPSHOT</version>
            </dependency>
    • 创建RefactorHelloController类继承hello-service-api中定义的HelloService接口,并参考之前的HelloController来实现这三个接口:
     1 @RestController
     2 public class RefactorHelloController implements HelloService {
     3     @Override
     4     public String hello(@RequestParam("name") String name) {
     5         return "Hello "+name;
     6     }
     7     //注解后面必须要有参数
     8     @Override
     9     public User hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age) {
    10         return new User(name,age);
    11     }
    12     @Override
    13     public String hello(@RequestBody User user) {
    14         return "Hello "+user.getName()+","+user.getAge();
    15     }
    16 
    17 }

      可以发现通过继承的方式,在Controller中不再包含以往会定义的请求映射注解@RequestMapping,而参数的注解定义在重写的时候会自动带过来。在这个类中,除了要实现接口逻辑之外,只需要再增加@RestController注解使该类成为一个REST接口类就大功告成了。

      接下来在服务消费者中

    • 在feign-consumer的pom.xml文件中,和服务提供者一样,新增对hello-service-api的依赖。
    • 创建RefactorHelloService接口,并继承hello-service-api包中的HelloService接口,然后添加@FeignClient注解来绑定服务
    @FeignClient("HELLO-SERVICE")
    public interface RefactorHelloService extends HelloService {
    
    }
    • 最后,在ConsumerController中,注入RefactorHelloService实例,并新增一个请求/feign-consumer3来触发对RefactorHelloService的实例的调用。
     1 @RestController
     2 public class ConsumeController {
     3     
     4     @Autowired
     5     HelloService helloService;
     6     @Autowired
     7     RefactorHelloService refactorHelloService;
     8     
     9     @RequestMapping(value="/feign-consumer",method=RequestMethod.GET)
    10     public String helloConsumer(){
    11         return helloService.hello();
    12     }
    13     
    14     @RequestMapping(value="/feign-consumer2",method=RequestMethod.GET)
    15     public String helloConsumer2(){
    16         StringBuilder sb = new StringBuilder();
    17         sb.append(helloService.hello()).append("
    ");
    18         sb.append(helloService.hello("LULU")).append("
    ");
    19         sb.append(helloService.hello("LULU",18)).append("
    ");
    20         sb.append(helloService.hello(new User("LULU",18))).append("
    ");
    21         return sb.toString();
    22     }
    23     
    24     @RequestMapping(value="/feign-consumer3",method=RequestMethod.GET)
    25     public String helloConsumer3(){
    26         StringBuilder sb = new StringBuilder();
    27         sb.append(refactorHelloService.hello("KINGKANG")).append("
    ");
    28         sb.append(refactorHelloService.hello("KINGKANG",18)).append("
    ");
    29         sb.append(refactorHelloService.hello(new com.kingbrook.dto.User("KINGKANG",18))).append("
    ");
    30         return sb.toString();
    31     }
    32 }

    测试

      由于提供者和消费者都依赖hello-service-api,所以必须先构建hello-service-api工程,接着我们分别启动服务注册中心,hello-service和feign-consumer,并访问http://localhost:8092/feign-consumer3,得到:

    至此,关于SpringCloud+Feign的参数绑定,和继承特性搭建成功!

    项目完整代码见https://github.com/Adosker/springCloudAllDemo


    注释一: @RequestParam和@PathVariable的区别就在于请求时当前参数是在url路由上还是在请求的body上,@RequestParam修饰的参数最后通过key=value的形式放在http请求的Body传过来 eg:http://xxxxx?kingName=xxx,作用相当于Request.getParameter() ,而后者http://xxxxx/kingName kingName即是参数又是路由

    @RequestBody能把简单json结构参数转换成实体类,@RequestHeader 注解可以把Request请求header部分的值绑定到方法的参数上

    注释二:数据传输对象(DTO)(Data Transfer Object)

    注释三:@RestController注解相当于@ResponseBody + @Controller合在一起的作用。Spring 4.0以后提供

  • 相关阅读:
    文档的几何形状和滚动
    聊聊并发——生产者消费者模式
    在JavaScript中什么时候使用==是正确的?
    HTML5使用canvas画图时,图片被自动放大模糊的问题
    获取元素的几种方式
    利用jQuery和CSS实现环形进度条
    最实用、最常用的jQuery代码片段
    表格样式
    javascript常量的定义
    null 和 undefined 的区别
  • 原文地址:https://www.cnblogs.com/king-brook/p/9549287.html
Copyright © 2011-2022 走看看