一般來說股冗,系統(tǒng)上線以后霹陡,需求仍會發(fā)生變動,功能也會迭代更新魁瞪∧侣桑可能是接口參數(shù)發(fā)生變更,也有可能是業(yè)務(wù)邏輯需要調(diào)整导俘,如果直接在原來的接口上進(jìn)行修改峦耘,必然會影響原有服務(wù)的正常運行。
常見的解決方案旅薄,是在接口路徑中加入版本號用于區(qū)分辅髓,此外還可以在參數(shù)甚至 header 里帶上版本號泣崩。這里以在請求路徑中帶上版本號為例,如:http://IP:PORT/api/v1/test 洛口,v1 即代表的是版本號矫付。當(dāng)然了,可以像這樣第焰,直接寫死在 @RequestMapping("api/v1/test")
屬性中买优,不過下面提供了更為優(yōu)雅的解決方案。
- 自定義版本號標(biāo)記注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
/**
* 標(biāo)識版本號挺举,從1開始
*/
int value() default 1;
}
- 重寫相應(yīng)的 RequestCondition
@Data
@Slf4j
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
/**
* 接口路徑中的版本號前綴杀赢,如: api/v[1-n]/test
*/
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+)/");
private int apiVersion;
ApiVersionCondition(int apiVersion) {
this.apiVersion = apiVersion;
}
@Override
public ApiVersionCondition combine(ApiVersionCondition other) {
// 最近優(yōu)先原則,方法定義的 @ApiVersion > 類定義的 @ApiVersion
return new ApiVersionCondition(other.getApiVersion());
}
@Override
public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if (m.find()) {
// 獲得符合匹配條件的ApiVersionCondition
int version = Integer.valueOf(m.group(1));
if (version >= getApiVersion()) {
return this;
}
}
return null;
}
@Override
public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
// 當(dāng)出現(xiàn)多個符合匹配條件的ApiVersionCondition湘纵,優(yōu)先匹配版本號較大的
return other.getApiVersion() - getApiVersion();
}
}
- 重寫部分 RequestMappingHandlerMapping 的方法
@Slf4j
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
// 掃描類上的 @ApiVersion
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createRequestCondition(apiVersion);
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
// 掃描方法上的 @ApiVersion
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createRequestCondition(apiVersion);
}
private RequestCondition<ApiVersionCondition> createRequestCondition(ApiVersion apiVersion) {
if (Objects.isNull(apiVersion)) {
return null;
}
int value = apiVersion.value();
Assert.isTrue(value >= 1, "Api Version Must be greater than or equal to 1");
return new ApiVersionCondition(value);
}
}
- 配置注冊自定義的 CustomRequestMappingHandlerMapping
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
@Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
return new CustomRequestMappingHandlerMapping();
}
}
- 編寫接口脂崔,標(biāo)記上相應(yīng)的
@ApiVersion
@Slf4j
@ApiVersion
@RestController
@RequestMapping("api/{version}/test")
public class TestController {
@GetMapping
public String test01(@PathVariable String version) {
return "test01 : " + version;
}
@GetMapping
@ApiVersion(2)
public String test02(@PathVariable String version) {
return "test02 : " + version;
}
}
- 啟動 Application,測試及查看結(jié)果
示例源碼
歡迎關(guān)注我的個人公眾號:超級碼里奧
如果這對您有幫助梧喷,歡迎點贊和分享砌左,轉(zhuǎn)載請注明出處