spring boot: 设计接口站api的版本号,支持次版本号(spring boot 2.3.2)
一,为什么接口站的api要使用版本号?
1,当服务端接口的功能发生改进后,
客户端如果不更新版本,
3,版本号需要能向下兼容,
如果访问一个高的版本不存在时,应该能访问到比它小的离它最近的版本,
因为如果每个入口或每个功能在有新版本时都要重新写一遍,
则和把代码复制一份没有两样了,
这样才可以方便的管理
4,版本号可以放到请求头和url中,
我们采用比较直观的放到url中的形式演示,
形如: /v1.2/home/page?
5,说明:生产环境中没发现有必要在次版本号后面再加一级,
所以有次版本号后已经够用了,
大家如果认为有必要增加一级的话,可以修改代码中VERSION_PREFIX_PATTERN的正则:
说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblogs.com/architectforest
对应的源码可以访问这里获取: https://github.com/liuhongdi/
说明:作者:刘宏缔 邮箱: 371125307@qq.com
二,演示项目的相关信息
1,项目地址:
https://github.com/liuhongdi/apiversion
2,项目说明:
我们给接口站的接口增加api的版本号
支持整数方式(例:v1)和次版本号形式(例:v1.3)
3,项目结构:如图:
三,java代码说明
1,ApiVersion.java
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
//版本号的值,从1开始
String value() default "1";
}
用来添加版本号的一个注解
2,ApiVersionCondition.java
//实现RequestCondition
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
//api版本号
private String apiVersion;
//版本号的格式,如: /v[1-n]/api/test or /v1.5/home/api
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v((\\d+\\.\\d+)|(\\d+))/");
public ApiVersionCondition(String apiVersion) {
this.apiVersion = apiVersion;
} //将不同的筛选条件进行合并
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
// 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
return new ApiVersionCondition(other.getApiVersion());
} //版本比对,用于排序
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
//优先匹配最新版本号
return compareTo(other.getApiVersion(),this.apiVersion)?1:-1;
} //获得符合匹配条件的ApiVersionCondition
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
String version = m.group(1);
if (compareTo(version,this.apiVersion)){
return this;
}
}
return null;
}
//compare version
private boolean compareTo(String version1,String version2){
if (!version1.contains(".")) {
version1 += ".0";
}
if (!version2.contains(".")) {
version2 += ".0";
}
String[] split1 = version1.split("\\.");
String[] split2 = version2.split("\\.");
for (int i = 0; i < split1.length; i++) {
if (Integer.parseInt(split1[i])<Integer.parseInt(split2[i])){
return false;
}
}
return true;
} public String getApiVersion() {
return apiVersion;
}
}
实现查找和比较版本号的功能
3,ApiVersionRequestMappingHandlerMapping.java
//扩展RequestMappingHandlerMapping
public class ApiVersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping { //类上有 @ApiVersion注解时生效
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createRequestCondition(apiVersion);
} //方法上有 @ApiVersion注解时生效
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createRequestCondition(apiVersion);
} //返回ApiVersionCondition
private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
}
}
继承RequestMappingHandlerMapping,在类上和方法上有注解时,
使用ApiVersionCondition进行处理
4,WebMvcConfig.java
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
//在获取RequestMappingHandlerMapping时
//返回我们自定义的ApiVersionRequestMappingHandlerMapping
@Override
protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
return new ApiVersionRequestMappingHandlerMapping();
}
}
把createRequestMappingHandlerMapping方法返回时使用我们自定义的ApiVersionRequestMappingHandlerMapping
5,HomeController.java
@RestController
@RequestMapping("/{version}/home")
public class HomeController { //匹配版本v1的访问
@ApiVersion("1")
@GetMapping
@RequestMapping("/home")
public String home01(@PathVariable String version) {
return "home v1 : version:" + version;
} //匹配版本v2的访问
@ApiVersion("2.0")
@GetMapping
@RequestMapping("/home")
public String home02(@PathVariable String version) {
return "home v2 version: " + version;
} //匹配版本v1.5-2.0的访问
@ApiVersion("1.5")
@GetMapping
@RequestMapping("/home")
public String home15(@PathVariable String version) {
return "home v1.5 version: " + version;
}
}
6,GoodsV1Controller和GoodsV2Controller
把版本号加到controller上,
和加在方法上的使用一样,
为节省篇幅,不再贴代码,大家可以自己去github上访问
四,测试效果
1,访问
http://127.0.0.1:8080/v1.0/home/home
返回:
home v1 : version:v1.0
访问
http://127.0.0.1:8080/v2/home/home
返回:
home v2 version: v2
可见版本号带不带点,不妨碍它访问到对应的版本
2,访问:
http://127.0.0.1:8080/v3.0/home/home
返回:
home v2 version: v3.0
访问:
http://127.0.0.1:8080/v1.14/home/home
返回:
home v1.5 version: v1.14
可见版本号可以自动访问到距它最近的版本的功能
五, 查看spring boot的版本
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.2.RELEASE)
spring boot: 设计接口站api的版本号,支持次版本号(spring boot 2.3.2)的更多相关文章
- Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一)
标题 Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(一) 技术 Spring Boot 2.Spring Security 5.JWT 运行环境 ...
- 品味Spring Cache设计之美
最近负责教育类产品的架构工作,两位研发同学建议:"团队封装的Redis客户端可否适配Spring Cache,这样加缓存就会方便多了" . 于是边查阅文档边实战,收获颇丰,写这篇文 ...
- spring boot:接口站增加api版本号后的安全增强(spring boot 2.3.3)
一,接口站增加api版本号后需要做安全保障? 1,如果有接口需要登录后才能访问的, 需要用spring security增加授权 2,接口站需要增加api版本号的检验,必须是系统中定义的版本号才能访问 ...
- spring boot使用swagger生成api接口文档
前言 在之前的文章中,使用mybatis-plus生成了对应的包,在此基础上,我们针对项目的api接口,添加swagger配置和注解,生成swagger接口文档 具体可以查看本站spring boot ...
- Spring Boot实战:Restful API的构建
上一篇文章讲解了通过Spring boot与JdbcTemplate.JPA和MyBatis的集成,实现对数据库的访问.今天主要给大家分享一下如何通过Spring boot向前端返回数据. 在现在的开 ...
- spring boot rest 接口集成 spring security(2) - JWT配置
Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...
- Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二)
Spring Boot+Spring Security+JWT 实现 RESTful Api 认证(二) 摘要 上一篇https://javaymw.com/post/59我们已经实现了基本的登录和t ...
- effective OC2.0 52阅读笔记(三 接口与API设计)
第三章:接口与API设计 15 用前缀避免命名空间冲突 总结:避免重名符号错误的唯一办法是变相实现命名空间.为所有符号都加上命名前缀.类和分类都应加三字前缀.注意类实现文件中的纯C函数及全局变量,是算 ...
- 学习设计接口api(转)
介绍 先说说啥是 Api 吧,以下摘自百度百科: API (Application Programming Interface,应用程序编程接口)是一些预先定义的函数,目的是提供应用程序与开发人员基于 ...
随机推荐
- 发现新世界:神级浏览器插件TamperMonkey(暴力猴)
由于谷歌浏览器各种受限 于是我就先使用火狐浏览器尝试此插件 步骤非常简单. 1.在火狐浏览器内打开如下网址:https://addons.mozilla.org/zh-CN/firefox/addon ...
- linux下redis安装部署
1.获取redis资源 进官网下载https://redis.io/download最新版本后将文件移到linux环境中 或者直接wget http://download.redis.io/relea ...
- 小白的springboot之路(十七)、阿里云OSS 的使用
0-前言 项目中,文件服务必不可少,常用的有各云服务商的OSS服务(如阿里云OSS,腾讯云OSS,七牛云).自建(fastDFS.minio): 推荐:如果用云服务的话,阿里云OSS很方便,如果自建的 ...
- Spring使用@Async实现异步
使用场景 在实际项目中,一个接口如果需要处理很多数据,如果是同步执行,通过网络请求接口可能会出现请求超时.这时候就需要使用异步执行处理了. 使用经验 代码 异步服务类 @Service // Spri ...
- Java架构师方案—多数据源开发详解及原理(二)(附完整项目代码)
1. mybatis下数据源开发工作 2. 数据源与DAO的关系原理模型 3. 为什么要配置SqlSessionTemplate类的bean 4. 多数据源应用测试 1. mybatis下数据源开发工 ...
- python中RGB色彩
turtle.colormode(mode)来改变色彩数值的使用 如果在修改颜色时写turtle.colormode(1.0) ,就需要使用RGB小数模式来去改变颜色 如果在修改颜色时写turtle. ...
- 4.案例 - NIO实现TCP通信
服务端: package cn.tedu.nio.channel; import java.net.InetSocketAddress; import java.nio.ByteBuffer; imp ...
- 1.Strom-概述
- Python爬取天气预报,Ta的城市开始降温了
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取 python免费学习资 ...
- 安装Angular CLI开发工具
目前,无论你使用什么前端框架,都必然要用到NodeJS工具,Angular也不例外,与其他框架不同的是,Angular一开始就使用"全家桶"式的设计思路,因此@angular/cl ...