??在我們的日常開(kāi)發(fā)中肋联,需求總是變化的锈麸。對(duì)于某個(gè)接口,隨著需求的升級(jí)牺蹄,也面臨里面邏輯的變化忘伞。例如,對(duì)于/v1/hello
,/v2/hello
兩個(gè)請(qǐng)求沙兰,若存在相應(yīng)的映射氓奈,則對(duì)應(yīng)入座。否則都映射到最新的接口上鼎天。則映射到最新的接口上舀奶。此時(shí),我們又想保持以前的接口還保留斋射,那么我們此時(shí)需要做的事育勺,把對(duì)接口的請(qǐng)求都映射到最新的接口上,而原來(lái)的接口請(qǐng)求還是映射原來(lái)的接口上罗岖。我在這里介紹用自定義注解的形式涧至,在@RequestMapping()
的映射原理上做文章。
- 自定義注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
int value();
}
??就是定義一個(gè)簡(jiǎn)單的注解@ApiVersion桑包,這個(gè)注解可以在類上和方法上都可以應(yīng)用南蓬。
- 注解的識(shí)別
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
@Override // ①
protected RequestCondition<ApiVesrsionCondition> getCustomTypeCondition(Class<?> handlerType) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
return createCondition(apiVersion);
}
@Override //②
protected RequestCondition<ApiVesrsionCondition> getCustomMethodCondition(Method method) {
ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
return createCondition(apiVersion);
}
//③ 實(shí)例化RequestCondition
private RequestCondition<ApiVesrsionCondition> createCondition(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVesrsionCondition(apiVersion.value());
}
}
??我們知道,光定義注解是沒(méi)什么用的哑了,重要的是我們識(shí)別到注解赘方,做相應(yīng)的事。RequestMappingHandlerMapping
類是與 @RequestMapping
相關(guān)的弱左,它定義映射的規(guī)則窄陡。即滿足怎樣的條件則映射到那個(gè)接口上。
??①處構(gòu)建類級(jí)的映射要求拆火,AnnotationUtils.findAnnotation
根據(jù)在類上面的注解實(shí)例化一個(gè)注解類跳夭。然后構(gòu)造RequestCondition鳖悠。
??②處構(gòu)建類級(jí)的映射要求,AnnotationUtils.findAnnotation
根據(jù)在方法上面的注解實(shí)例化一個(gè)注解類优妙。然后構(gòu)造RequestCondition乘综。
AnnotationUtils.findAnnotation
是用到Spring的工具類,根據(jù)標(biāo)注的注解識(shí)別注解套硼。很方便卡辰,比通過(guò)反射的方式來(lái)找到注解要方便。
- 自定義條件類
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;
}
//將不同的篩選條件合并,這里采用的覆蓋九妈,即后來(lái)的規(guī)則生效
public ApiVesrsionCondition combine(ApiVesrsionCondition other) {
return new ApiVesrsionCondition(other.getApiVersion());
}
//根據(jù)request查找匹配到的篩選條件
public ApiVesrsionCondition getMatchingCondition(HttpServletRequest request) {
System.out.println(request.getRequestURI());
Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());
if(m.find()){
Integer version = Integer.valueOf(m.group(1));
if(version >= this.apiVersion) // 如果請(qǐng)求的版本號(hào)大于配置版本號(hào), 則滿足雾鬼,即與請(qǐng)求的
return this;
}
return null;
}
//實(shí)現(xiàn)不同條件類的比較萌朱,從而實(shí)現(xiàn)優(yōu)先級(jí)排序
public int compareTo(ApiVesrsionCondition other, HttpServletRequest request) {
return other.getApiVersion() - this.apiVersion;
}
public int getApiVersion() {
return apiVersion;
}
}
??getMatchingCondition()
利用正則表達(dá)式把請(qǐng)求路徑中的/v1/hello
中的1(版本號(hào))找出來(lái),然后返回與大于等于1的條件類策菜。那么@RequestMapping
則路由到產(chǎn)生該條件類的方法下晶疼。
- 測(cè)試
@RequestMapping("/{version}/")
@Controller
public class VersionController {
@RequestMapping("hello")
@ApiVersion(1)
@ResponseBody
public String hello(){
System.out.println("haha1..........");
return "hello version1";
}
@RequestMapping("hello")
@ApiVersion(2)
@ResponseBody
public String hello2(){
System.out.println("haha2.........");
return "hello version2";
}
@RequestMapping("hello")
@ResponseBody
@ApiVersion(5)
public String hello5(){
System.out.println("haha5.........");
return "hello version5";
}
@RequestMapping("test")
@ResponseBody
public String test(){
return "test";
}
}
??每個(gè)方法上都用 @ApiVersion()
注解啦。當(dāng)在瀏覽器輸入http://localhost:8761/v2/hello
則跳到hello2()
方法中執(zhí)行又憨。當(dāng)在瀏覽器輸入http://localhost:8761/v5/hello
則跳到hello5()
中執(zhí)行翠霍,因?yàn)楸?大的都調(diào)到最新hello5()
執(zhí)行。