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以后提供

  • 相关阅读:
    107. Binary Tree Level Order Traversal II
    108. Convert Sorted Array to Binary Search Tree
    111. Minimum Depth of Binary Tree
    49. Group Anagrams
    使用MALTAB标定实践记录
    442. Find All Duplicates in an Array
    522. Longest Uncommon Subsequence II
    354. Russian Doll Envelopes
    opencv 小任务3 灰度直方图
    opencv 小任务2 灰度
  • 原文地址:https://www.cnblogs.com/king-brook/p/9549287.html
Copyright © 2011-2022 走看看