- 推荐序一
- 推荐序二
- 推荐序三
- 推荐语
- 前言
- 第1章 基础知识
- 第2章 微服务构建:Spring Boot
- 第3章 服务治理:Spring Cloud Eureka
- 第4章 客户端负载均衡:Spring Cloud Ribbon
- 第5章 服务容错保护:Spring Cloud Hystrix
- 第6章 声明式服务调用:Spring Cloud Feign
- 第7章 API网关服务:Spring Cloud Zuul
- 第8章 分布式配置中心:Spring Cloud Config
- 第9章 消息总线:Spring Cloud Bus
- 第10章 消息驱动的微服务:Spring Cloud Stream
- 附录 A Starter POMs
- 后记
继承特性
通过“快速入门”以及“参数绑定”小节中的示例实践,相信很多读者已经观察到,当使用 Spring MVC 的注解来绑定服务接口时,我们几乎完全可以从服务提供方的Controller中依靠复制操作,构建出相应的服务客户端绑定接口。既然存在这么多复制操作,我们自然需要考虑这部分内容是否可以得到进一步的抽象呢?在Spring Cloud Feign中,针对该问题提供了继承特性来帮助我们解决这些复制操作,以进一步减少编码量。下面,我们详细看看如何通过Spring Cloud Feign的继承特性来实现REST接口定义的复用。
- 为了能够复用 DTO 与接口定义,我们先创建一个基础的 Maven 工程,命名为hello-service-api。
- 由于在 hello-service-api 中需要定义可同时复用于服务端与客户端的接口,我们要使用到 Spring MVC 的注解,所以在 pom.xml 中引入 spring-bootstarter-web依赖,具体内容如下所示:
<groupId>com.didispace</groupId>
<artifactId>hello-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>hello-service-api</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.7.RELEASE</version>
<relativePath/> <!--lookup parent from repository-->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
- 将上一节中实现的 User 对象复制到 hello-service-api 工程中,比如保存到com.didispace.dto.User。
- 在hello-service-api工程中创建com.didispace.service.HelloService接口,内容如下,该接口中的User对象为本项目中的com.didispace.dto.User。
- 将上一节中实现的 User 对象复制到 com.didispace.dto.User。创建com.didispace.service.HelloService 接口,内容如下,该接口中的 User对象为本项目中的com.didispace.dto.User。
@RequestMapping("/refactor")
public interface HelloService {
@RequestMapping(value="/hello4",method=RequestMethod.GET)
String hello(@RequestParam("name")String name);
@RequestMapping(value="/hello5",method=RequestMethod.GET)
User hello(@RequestHeader("name")String name,@RequestHeader("age")Integer age);
@RequestMapping(value="/hello6",method=RequestMethod.POST)
String hello(@RequestBody User user);
}
因为后续还会通过之前的hello-service和feign-consumer来重构,所以为了避免接口混淆,在这里定义HelloService时,除了头部定义了/rafactor前缀之外,同时将提供服务的三个接口更名为/hello4、/hello5、/hello6。
- 下面对 hello-service 进行重构,在 pom.xml 的 dependency 节点中,新增对hello-service-api的依赖。
<dependency>
<groupId>com.didispace</groupId>
<artifactId>hello-service-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
- 创建 RefactorHelloController 类继承 hello-service-api 中定义的HelloService接口,并参考之前的HelloController来实现这三个接口,具体内容如下所示:
@RestController
public class RefactorHelloController implements HelloService {
@Override
public String hello(@RequestParam("name")String name){
return "Hello "+name;
}
@Override
public User hello(@RequestHeader("name")String name,@RequestHeader("age")Integer age){
return new User(name,age);
}
@Override
public String hello(@RequestBody User user){
return "Hello "+user.getName()+","+user.getAge();
}
}
我们可以看到通过继承的方式,在 Controller 中不再包含以往会定义的请求映射注解@RequestMapping,而参数的注解定义在重写的时候会自动带过来。在这个类中,除了要实现接口逻辑之外,只需再增加@RestController注解使该类成为一个REST接口类就大功告成了。
- 完成了服务提供者的重构,接下来在服务消费者 feign-consumer 的 pom.xml文件中,如在服务提供者中一样,新增对hello-service-api的依赖。
- 创建 RefactorHelloService 接口,并继承 hello-service-api 包中的HelloService接口,然后添加@FeignClient注解来绑定服务。
@FeignClient(value="HELLO-SERVICE")
public interface RefactorHelloService extends com.didispace.service.HelloService {
}
- 最后,在ConsumerController中,注入RefactorHelloService的实例,并新增一个请求/feign-consumer3来触发对RefactorHelloService的实例的调用。
@Autowired
RefactorHelloService refactorHelloService;
@RequestMapping(value="/feign-consumer3",method=RequestMethod.GET)
public String helloConsumer3(){
StringBuilder sb=new StringBuilder();
sb.append(refactorHelloService.hello("MIMI")).append("\n");
sb.append(refactorHelloService.hello("MIMI",20)).append("\n");
sb.append(refactorHelloService.hello(new com.didispace.dto.User("MIMI",20))).append("\n");
return sb.toString();
}
测试验证
这次的验证过程需要注意几个工程的构建顺序,由于 hello-service 和feign-consumer都依赖hello-service-api工程中的接口和DTO定义,所以必须先构建hello-service-api工程,然后再构建hello-service和feign-consumer。接着我们分别启动服务注册中心,hello-service 和 feign-consumer,并访问http://localhost:9001/feign-consumer3,调用成功后可以获得如下输出:
Hello MIMI
name=MIMI,age=20
Hello MIMI,20
优点与缺点
使用Spring Cloud Feign继承特性的优点很明显,可以将接口的定义从Controller中剥离,同时配合Maven私有仓库就可以轻易地实现接口定义的共享,实现在构建期的接口绑定,从而有效减少服务客户端的绑定配置。这么做虽然可以很方便地实现接口定义和依赖的共享,不用再复制粘贴接口进行绑定,但是这样的做法使用不当的话会带来副作用。由于接口在构建期间就建立起了依赖,那么接口变动就会对项目构建造成影响,可能服务提供方修改了一个接口定义,那么会直接导致客户端工程的构建失败。所以,如果开发团队通过此方法来实现接口共享的话,建议在开发评审期间严格遵守面向对象的开闭原则,尽可能地做好前后版本的兼容,防止牵一发而动全身的后果,增加团队不必要的维护工作量。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论