2019独角兽企业重金招聘Python工程师标准>>>

原文:http://www.cnblogs.com/jcli/p/springmvc_restful_version.html

需求

移动互联网时代的到来,软件开发的模式也在变化。记得以前做B/S的后台开发,基本上没有Http接口一说,全部是通过渲染模板技术(jsp,freemark)把最终html展示给最终用户。现在完全变了,基于后台接口提供方,我们从来不是针对只是浏览器展示的后台输出,而是各种终端,比如android,ios。所以设计接口的时候一定要小心,一旦放出去的接口可能就永远都难以变动(除非你强制客户端用户升级)。我们知道, Restful API已经成为接口设计的一个业务准则。如果你还不是很清楚什么是Restful,推荐你看一下这篇文章:  RESTful API 设计指南 。其实,我们就是设计一套基于http协议的业务接口,但是随着时间变迁,业务的变化,或者我们协议本身的优化,都有可能要改变之前存在的接口。这时候给所有接口进行版本管理就显得很重要了,比如某个添加用户的接口,由于业务发展很大,接口的字段属性变化很大,只能重新定义一个新的接口,由 /v1/user/add 变成了 /v2/user/add,这样我们就要维护两套接口的逻辑,映射到代码里,就是要维护两个不同的业务方法。所以这篇文章主要讲的是基于SpringMVC开发的应用,怎么通过扩展开发来方便我们在代码层级管理各不同的版本接口。

SpringMVC原理概述

SpringMVC核心思想就是通过一个servlet(DispatchServlet)把请求转发到各个执行方法上(Controller的method),截张官方的图如下: 就是把某个形式的URL(当然,url不是唯一的决定条件,还有比如请求方法,get还是post,请求头中的信息)映射到某个类的具体方法上,这个核心的组件在SpringMVC中叫做: HandlerMapping。我们一般在spring的config文件中做如下配置时会自动初始化加载一个HanlderMapping的实现类:RequestMappingHandlerMapping:

<mvc:annotation-driven/>

至于这个一行的配置干了什么,可以从org.springframework.web.servlet.config.MvcNamespaceHandler这个类开始看进去。我们现在来定义一个Controller,如下:

@Controller
public class HelloController {@RequestMapping("hello/")@ResponseBodypublic String hello(HttpServletRequest request){System.out.println("haha1..........");return "hello";}
}

这样我们通过 /hello/ 就可以调用了。现在假如我们针对这个接口的业务出现了很大的变化(涉及到字段,报文的改变,和之前的不能兼容),但是老的接口又不能废弃,因为你不能保证放出去的接口没有人调用。所以我们只能把代码改成如下支持多个版本接口:

@Controller
public class HelloController {@RequestMapping("v1/hello/")@ResponseBodypublic String hello1(HttpServletRequest request){System.out.println("haha1..........");return "hello";}@RequestMapping("v2/hello/")@ResponseBodypublic String hello2(HttpServletRequest request){System.out.println("haha2.........");return "hello";}
}

现在我们就可以通过 /v1/hello, /v2/hello 来分别访问v1和v2两个版本对应的接口了。这看起来好像可以解决问题,因为我们每次某个接口有变动,只要新写一个对应该版本的方法就可以了。但是相应的问题也就来了:

  • 我们一般发布出去的接口,都是以http://api.custom.com/v1,http://api.custom.com/v2发布出去的,从v1到v2,往往我们只会变动其中一小部分接口,但是客户端必需统一版本号调用 。
  • 不能智能向上兼容接口。如果现在我们某个接口最高版本是v2,如 /v2/hello, 现在通过 /v3/hello 要能够自动适配到 /v2/hello上。

所以我们通过Spring强大的扩展机制增加几个扩展类来完成这个工作。先看下SringMVC中HandlerMapping加载初始化和动态根据url到handler的流程: 可以看到,HandlerMapping就是通过继承InitializingBean接口在完成实例后,扫描所有的Controller和标识RequestMapping的方法,缓存这个映射对应关系。然后在应用运行的时候,根据请求的request来找到相应的handler来处理这个请求。所以,我们添加扩展类:

  • ApiVersion
  • ApiVesrsionCondition
  • CustomRequestMappingHandlerMapping
  • WebConfig

现分别来看下这个类,首先看下ApiVersion这个注解:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {/*** 版本号* @return*/int value();
}

这个注解用来标识某个类或者方法要处理的对应版本号,使用如下:

@Controller
@RequestMapping("/{version}/")
public class HelloController {@RequestMapping("hello/")@ApiVersion(1)@ResponseBodypublic String hello(HttpServletRequest request){System.out.println("haha1..........");return "hello";}@RequestMapping("hello/")@ApiVersion(2)@ResponseBodypublic String hello2(HttpServletRequest request){System.out.println("haha2.........");return "hello";}@RequestMapping("hello/")@ApiVersion(5)@ResponseBodypublic String hello5(HttpServletRequest request){System.out.println("haha5.........");return "hello";}
}

现在我们就可以通过 /v1/hello/, /v2/hello/, /v5/hello来分别调用版本1,2,5的管理。当然我们也要解决刚才说的两点问题,如果用户通过 /v4/hello/来访问接口,则要自动适配到 /v2/hello/,因为 v2是比v4低的版本中最新的版本。

再来看下 ApiVersionCondition 这个类。这个类就是我们自定义一个条件筛选器,让SpringMVC在原有逻辑的基本上添加一个版本号匹配的规则:

public class ApiVesrsionCondition implements RequestCondition<ApiVesrsionCondition> {// 路径中版本的前缀, 这里用 /v[1-9]/的形式private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");private int apiVersion;public ApiVesrsionCondition(int apiVersion){this.apiVersion = apiVersion;}public ApiVesrsionCondition combine(ApiVesrsionCondition other) {// 采用最后定义优先原则,则方法上的定义覆盖类上面的定义return new ApiVesrsionCondition(other.getApiVersion());}public ApiVesrsionCondition getMatchingCondition(HttpServletRequest request) {Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getPathInfo());if(m.find()){Integer version = Integer.valueOf(m.group(1));if(version >= this.apiVersion) // 如果请求的版本号大于配置版本号, 则满足return this;}return null;}public int compareTo(ApiVesrsionCondition other, HttpServletRequest request) {// 优先匹配最新的版本号return other.getApiVersion() - this.apiVersion;}public int getApiVersion() {return apiVersion;}}

要把这个筛选规则生效的话,要扩展原胡的HandlerMapping,把这个规则设置进去生效,看下CustomRequestMappingHandlerMapping的代码:

public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {@Overrideprotected RequestCondition<ApiVesrsionCondition> getCustomTypeCondition(Class<?> handlerType) {ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);return createCondition(apiVersion);}@Overrideprotected RequestCondition<ApiVesrsionCondition> getCustomMethodCondition(Method method) {ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);return createCondition(apiVersion);}private RequestCondition<ApiVesrsionCondition> createCondition(ApiVersion apiVersion) {return apiVersion == null ? null : new ApiVesrsionCondition(apiVersion.value());}
}

最后,得让SpringMVC加载我们定义的CustomRequestMappingHandlerMapping以覆盖原先的RequestMappingHandlerMapping, 所以要去掉前面说的<mvc:annotation-driven/>这个配置,我们通过JavaConfig的方式注入:

@Configuration
public class WebConfig extends WebMvcConfigurationSupport{@Override@Beanpublic RequestMappingHandlerMapping requestMappingHandlerMapping() {RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();handlerMapping.setOrder(0);handlerMapping.setInterceptors(getInterceptors());return handlerMapping;}
}

Over!

详细代码:  https://github.com/hongfuli/study_notes/tree/master/spring/samples   参考: http://stackoverflow.com/questions/10312177/how-to-implement-requestmapping-custom-properties/10336769#10336769 https://jira.spring.io/browse/SPR-9344

tips:

本文由wp2Blog导入,原文链接:http://devonios.com/%e4%bd%bf%e7%94%a8springmvc%e5%88%9b%e5%bb%ba%e6%94%af%e6%8c%81%e5%90%91%e4%b8%8b%e5%85%bc%e5%ae%b9%e7%9a%84%e7%89%88%e6%9c%ac%e5%8c%96%e7%9a%84api%e6%8e%a5%e5%8f%a3.html

转载于:https://my.oschina.net/yangyan/blog/859489

使用SpringMVC创建支持向下兼容的版本化的API接口相关推荐

  1. 切勿版本化Web API

    在最近的一次演讲中,Sebastien Lambla指出,通过在URI中增加版本或者使用带有版本的媒体类型将Web API版本化在开放网络上是行不通的.我们真正需要的是,协定随着我们需要的变化而演化. ...

  2. 微信小程序体验版无法调用API接口,显示空白

    微信小程序体验版无法调用接口 解决方案: 当扫码之后,前往体验版,右上角三个点,点击一,然后划到最后,点击开发调试,打开调试,完美解决.

  3. 版本化SQL Server数据库

    本文翻译自:Versioning SQL Server database I want to get my databases under version control. 我想让我的数据库受版本控制 ...

  4. KubeVela 1.3 发布:开箱即用的可视化应用交付平台,引入插件生态、权限认证、版本化等企业级新特性

    作者:KubeVela 社区 得益于 KubeVela 社区上百位开发者的参与和 30 多位核心贡献者的 500 多次代码提交, KubeVela 1.3 版本正式发布.相较于三个月前发布的 v1.2 ...

  5. IPFS - 可快速索引的版本化的点对点文件系统(草稿3)

    摘要 星际文件系统是一种点对点的分布式文件系统, 旨在连接所有有相同的文件系统的计算机设备.在某些方面, IPFS类似于web, 但web 是中心化的,而IPFS是一个单一的Bittorrent 群集 ...

  6. 如何版本化你的API?--转

    原文地址:http://www.infoq.com/cn/news/2017/09/How-versioning-API 如何版本化API需要考虑各种实际业务场景,但是一个完备的API应该是: 和客户 ...

  7. springMVC+Spring+mybatis整合配置版与注解版

    springMVC+Spring+mybatis整合 , 使用配置版来完成: -----------------------------前端 1.创建web系统,导入jar包: spring的jar包 ...

  8. MongoDB 5.0 来了,原生时序、版本化 API 新特性悉数登场

    作者 | 伍杏玲 出品 | CSDN云计算(ID:CSDNcloud) 据 DB-Engines 数据库最新 7 月流行度排行榜显示,前五名十分稳定:Oracle.MySQL.Microsoft SQ ...

  9. 老视频修复软件,Topaz Video Enhance Al可以视频无损放大,支持win/mac版

    老视频修复软件,Topaz Video Enhance Al可以视频无损放大,支持win/mac版,视频放大传统的视频放大仅会扩展分辨率,降低质量并破坏细节.从来没有一种方法可以从低分辨率素材中完美地 ...

最新文章

  1. 各系统QT安装ROS后不显示src问题
  2. java 弱引用 集合_java 弱引用集合类WeakHashMap
  3. leaflets + heatmap 加载地图
  4. ajax form表单提交_开发日志:金数据表单自动提交脚本
  5. python redis模块_python redis 模块 官方文档(中)
  6. kotlin 查找id_Kotlin程序查找等边三角形的区域
  7. Maya Calendar
  8. 云小课 | ModelArts Pro 自然语言处理套件:高效构建行业高精度文本处理模型
  9. homebrew安装
  10. TensorFlow读取自己数据集的几个小方法
  11. web网页规划与设计:网站设计——简洁的旅游酒店公寓(5页) HTML+CSS+JavaScript
  12. 区块链技术与应用(北大公开课,肖臻)- ETH 反思
  13. win7系统电脑蓝屏怎么解决,如何解决win7电脑蓝屏
  14. 51单片机仿真例程-八段数码管
  15. 三重积分平均值_二重积分或者三重积分里面如果积分区域关于坐标轴对称比如积分区域是一个圆或者球,就只用求第一象限或卦...
  16. 百兆宽带是怎么个一百兆?
  17. 调用方法有抛出异常的解决办法
  18. oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
  19. WPA入门(一) —— 到底为什么这么慢?
  20. 单片机c语言生日快乐歌,单片机C语言程序设计:播放生日快乐歌

热门文章

  1. 迪士尼公布最新研究:AR对象可智能地与环境中的物体交互
  2. 英特尔10nm至强CPU发布,对标AMD“米兰”EPYC,然而结果尴尬了
  3. 2020中国人工智能年度评选开启,4大类别7大奖项申报正式启动
  4. 逆向工程、软件后门……原来美剧《硅谷》里藏着这么多知识点
  5. 国产游戏《黑神话:悟空》刷爆全网,虚幻引擎4打造,网友:有生之年终于看到国产3A!...
  6. (原創) array可以使用reference方式傳進function嗎? (C/C++)
  7. 开源跨平台移动项目Ngui【Action动作系统】
  8. 启动oracle em命令
  9. 2017-2021年中国大数据产业预测分析及全球市场规模预测
  10. (二十)java多线程之ScheduledThreadPoolExecutor