SpringBoot實現(xiàn)API接口的完整代碼
一、簡介
產(chǎn)品迭代過程中,同一個接口可能同時存在多個版本,不同版本的接口URL、參數(shù)相同,可能就是內(nèi)部邏輯不同。尤其是在同一接口需要同時支持舊版本和新版本的情況下,比如APP發(fā)布新版本了,有的用戶可能不選擇升級,這是后接口的版本管理就十分必要了,根據(jù)APP的版本就可以提供不同版本的接口。
二、代碼實現(xiàn)
本文的代碼實現(xiàn)基于SpringBoot 2.3.4-release
1.定義注解
ApiVersion
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
/**
* 版本。x.y.z格式
*
* @return
*/
String value() default "1.0.0";
}
value值默認(rèn)為1.0.0
EnableApiVersion
/**
* 是否開啟API版本控制
*/
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import(ApiAutoConfiguration.class)
public @interface EnableApiVersion {
}
在啟動類上添加這個注解后就可以開啟接口的多版本支持。使用Import引入配置ApiAutoConfiguration。
2.將版本號抽象為ApiItem類
ApiItem
@Data
public class ApiItem implements Comparable<ApiItem> {
private int high = 1;
private int mid = 0;
private int low = 0;
public static final ApiItem API_ITEM_DEFAULT = ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION);
public ApiItem() {
}
@Override
public int compareTo(ApiItem right) {
if (this.getHigh() > right.getHigh()) {
return 1;
} else if (this.getHigh() < right.getHigh()) {
return -1;
}
if (this.getMid() > right.getMid()) {
return 1;
} else if (this.getMid() < right.getMid()) {
return -1;
}
if (this.getLow() > right.getLow()) {
return 1;
} else if (this.getLow() < right.getLow()) {
return -1;
}
return 0;
}
}
為了比較版本號的大小,實現(xiàn)Comparable接口并重寫compareTo(),從高位到低位依次比較。
ApiConverter
public class ApiConverter {
public static ApiItem convert(String api) {
ApiItem apiItem = new ApiItem();
if (StringUtils.isBlank(api)) {
return apiItem;
}
String[] cells = StringUtils.split(api, ".");
apiItem.setHigh(Integer.parseInt(cells[0]));
if (cells.length > 1) {
apiItem.setMid(Integer.parseInt(cells[1]));
}
if (cells.length > 2) {
apiItem.setLow(Integer.parseInt(cells[2]));
}
return apiItem;
}
}
ApiConverter提供靜態(tài)方法將字符創(chuàng)轉(zhuǎn)為ApiItem。
常量類,定義請求頭及默認(rèn)版本號
public class ApiVersionConstant {
/**
* header 指定版本號請求頭
*/
public static final String API_VERSION = "x-api-version";
/**
* 默認(rèn)版本號
*/
public static final String DEFAULT_VERSION = "1.0.0";
}
3.核心ApiCondition
新建ApiCondition類,實現(xiàn)RequestCondition,重寫combine、getMatchingCondition、compareTo方法。
RequestCondition
public interface RequestCondition<T> {
/**
* 方法和類上都存在相同的條件時的處理方法
*/
T combine(T other);
/**
* 判斷是否符合當(dāng)前請求,返回null表示不符合
*/
@Nullable
T getMatchingCondition(HttpServletRequest request);
/**
*如果存在多個符合條件的接口,則會根據(jù)這個來排序,然后用集合的第一個元素來處理
*/
int compareTo(T other, HttpServletRequest request);
以上對RequestCondition簡要說明,后續(xù)詳細(xì)源碼分析各個方法的作用。
ApiCondition
@Slf4j
public class ApiCondition implements RequestCondition<ApiCondition> {
public static ApiCondition empty = new ApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION));
private ApiItem version;
private boolean NULL;
public ApiCondition(ApiItem item) {
this.version = item;
}
public ApiCondition(ApiItem item, boolean NULL) {
this.version = item;
this.NULL = NULL;
}
/**
* <pre>
* Spring先掃描方法再掃描類,然后調(diào)用{@link #combine}
* 按照方法上的注解優(yōu)先級大于類上注解的原則處理,但是要注意如果方法上不定義注解的情況。
* 如果方法或者類上不定義注解,我們會給一個默認(rèn)的值{@code empty},{@link ApiHandlerMapping}
* </pre>
* @param other 方法掃描封裝結(jié)果
* @return
*/
@Override
public ApiCondition combine(ApiCondition other) {
// 選擇版本最大的接口
if (other.NULL) {
return this;
}
return other;
}
@Override
public ApiCondition getMatchingCondition(HttpServletRequest request) {
if (CorsUtils.isPreFlightRequest(request)) {
return empty;
}
String version = request.getHeader(ApiVersionConstant.API_VERSION);
// 獲取所有小于等于版本的接口;如果前端不指定版本號,則默認(rèn)請求1.0.0版本的接口
if (StringUtils.isBlank(version)) {
log.warn("未指定版本,使用默認(rèn)1.0.0版本。");
version = ApiVersionConstant.DEFAULT_VERSION;
}
ApiItem item = ApiConverter.convert(version);
if (item.compareTo(ApiItem.API_ITEM_DEFAULT) < 0) {
throw new IllegalArgumentException(String.format("API版本[%s]錯誤,最低版本[%s]", version, ApiVersionConstant.DEFAULT_VERSION));
}
if (item.compareTo(this.version) >= 0) {
return this;
}
return null;
}
@Override
public int compareTo(ApiCondition other, HttpServletRequest request) {
// 獲取到多個符合條件的接口后,會按照這個排序,然后get(0)獲取最大版本對應(yīng)的接口.自定義條件會最后比較
int compare = other.version.compareTo(this.version);
if (compare == 0) {
log.warn("RequestMappingInfo相同,請檢查!version:{}", other.version);
}
return compare;
}
}
3.配置類注入容器
ApiHandlerMapping
public class ApiHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return buildFrom(AnnotationUtils.findAnnotation(handlerType, ApiVersion.class));
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return buildFrom(AnnotationUtils.findAnnotation(method, ApiVersion.class));
}
private ApiCondition buildFrom(ApiVersion platform) {
return platform == null ? getDefaultCondition() :
new ApiCondition(ApiConverter.convert(platform.value()));
}
private ApiCondition getDefaultCondition(){
return new ApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION),true);
}
}
ApiAutoConfiguration
public class ApiAutoConfiguration implements WebMvcRegistrations {
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new ApiHandlerMapping();
}
}
ApiAutoConfiguration沒有使用Configuration自動注入,而是使用Import帶入,目的是可以在程序中選擇性啟用或者不啟用版本控制。
三、原理解析
四、總結(jié)
到此這篇關(guān)于SpringBoot實現(xiàn)API接口的文章就介紹到這了,更多相關(guān)SpringBoot實現(xiàn)API接口內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot3整合SpringDoc OpenAPI生成接口文檔的詳細(xì)過程
- 關(guān)于springboot忽略接口,參數(shù)注解的使用ApiIgnore
- Springboot+Redis實現(xiàn)API接口防刷限流的項目實踐
- SpringBoot?快速實現(xiàn)?api?接口加解密功能
- 詳解Springboot快速搭建跨域API接口的步驟(idea社區(qū)版2023.1.4+apache-maven-3.9.3-bin)
- SpringBoot整合Sa-Token實現(xiàn)?API?接口簽名安全校驗功能
- SpringBoot如何根據(jù)目錄結(jié)構(gòu)生成API接口前綴
- SpringBoot可視化接口開發(fā)工具magic-api的簡單使用教程
- springboot接入方式對接股票數(shù)據(jù)源API接口的操作方法
相關(guān)文章
SpringBoot應(yīng)用啟動慢的原因分析及優(yōu)化方法
在使用Spring Boot進(jìn)行開發(fā)時,快速啟動應(yīng)用程序是一個非常重要的需求,然而,在某些情況下,我們會遇到Spring Boot應(yīng)用啟動緩慢的問題,本文將分析Spring Boot應(yīng)用啟動慢的常見原因,并提供一些優(yōu)化方法,需要的朋友可以參考下2024-08-08
Win10系統(tǒng)下配置java環(huán)境變量的全過程
這篇文章主要給大家介紹了關(guān)于Win10系統(tǒng)下配置java環(huán)境變量的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
RestTemplate發(fā)送form-data請求上傳rul資源文件及對象參數(shù)方式
這篇文章主要介紹了RestTemplate發(fā)送form-data請求上傳rul資源文件及對象參數(shù)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-01-01
Java中字符串截取方法詳解及實際應(yīng)用小結(jié)
java中截取字符串的常用方法是使用String類的substring方法,本文通過實例代碼給大家介紹Java中字符串截取方法詳解及實際應(yīng)用小結(jié),感興趣的朋友跟隨小編一起看看吧2024-12-12
Java如何將處理完異常之后的程序能夠從拋出異常的地點向下執(zhí)行?
今天小編就為大家分享一篇關(guān)于Java如何將處理完異常之后的程序能夠從拋出異常的地點向下執(zhí)行?,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-04-04

