springboot接口如何控制版本?

首页 / 新闻资讯 / 正文

background-shape background-shape background-shape background-shape background-shape background-shape

dubbo有一套自己的版本控制策略,消费者根据服务提供者的version进行配置,最终实现版本控制,在springboot中又如何实现?

springboot中控制版本,其实很简单粗暴,不同版本的接口,可以直接写两个接口,接口uri不一样,不就OK了吗?但是为了让同一个项目组的同学都能按照一定的版本规则写接口,我们需要稍微做点加工。
简单粗暴版:
/api/query/studentInfo1
/api/query/studentInfo2

规范版:
/v1/api/query/studentInfo
/v2/api/query/studentInfo

具体实现:

需要继承RequestMappingHandlerMapping类,重写org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod方法。RequestMappingHandlerMapping是根据类或方法上的 @RequestMapping 来生成 RequestMappingInfo 的实例,负责根据用户请求(uri)匹配找到Handler即处理器(controller层加了RequestMapping注解的方法)。

package com.cn.dl.springbootdemo.annotation;  import org.springframework.core.annotation.AliasFor;  import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;  /**  * @author yanshao  * @date 2022/8/11 10:31 上午  */ @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface ApiVersion {      @AliasFor("path")     String[] value() default {};      @AliasFor("value")     String[] path() default {};  } 

这块代码有较详细的注释,最后自己debug跟一下spring源码

package com.cn.dl.springbootdemo.handler;  import com.cn.dl.springbootdemo.annotation.ApiVersion; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  import java.lang.reflect.Method; import java.util.Objects;  /**  * @author yanshao  * @date 2022/8/11 10:32 上午  */ public class ApiVersionHandlerMapping extends RequestMappingHandlerMapping {      @Override     protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mappingInfo) {         //类上加了ApiVersion注解         ApiVersion apiVersion = AnnotationUtils.findAnnotation(method.getDeclaringClass(), ApiVersion.class);          //可同时指定多个版本,类似于org.springframework.web.bind.annotation.RequestMapping.value的paths         String[] urlPatterns = Objects.isNull(apiVersion) ? new String[0] : apiVersion.value();          //api版本urlPatterns         PatternsRequestCondition apiVersionPattern = new PatternsRequestCondition(urlPatterns);          //当前未增加版本的paths,例如:/api/query/studentInfo         PatternsRequestCondition curtPattern = mappingInfo.getPatternsCondition();          //加版本号增加到curtPath之前,例如:/v1 + /api/query/studentInfo -> /v1/api/query/studentInfo         PatternsRequestCondition updatedFinalPattern = apiVersionPattern.combine(curtPattern);          //构建新的RequestMappingInfo         mappingInfo = new RequestMappingInfo(                 mappingInfo.getName(),                 updatedFinalPattern,                 mappingInfo.getMethodsCondition(),                 mappingInfo.getParamsCondition(),                 mappingInfo.getHeadersCondition(),                 mappingInfo.getConsumesCondition(),                 mappingInfo.getProducesCondition(),                 mappingInfo.getCustomCondition()         );         super.registerHandlerMethod(handler, method, mappingInfo);     } } 
package com.cn.dl.springbootdemo.handler;  import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  /**  * @author yanshao  * @date 2022/8/11 10:36 上午  */ @SpringBootConfiguration public class MvcMappingConfig implements WebMvcRegistrations {      //注册ApiVersionHandlerMapping组件     @Override     public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {         return new ApiVersionHandlerMapping();     } } 
package com.cn.dl.springbootdemo.controller;  import com.alibaba.fastjson.JSONObject; import com.cn.dl.springbootdemo.annotation.ApiVersion; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  /**  * @author yanshao  * @date 2022/8/11 10:37 上午  */ @RestController @ApiVersion("v1") @RequestMapping("/api/") public class StudentController {      @GetMapping(value = "/query/studentInfo")     public JSONObject studentInfo(){         JSONObject result = new JSONObject();         result.put("name","yanshao");         result.put("age",27);         result.put("apiVersion","v1");         return result;     } } 
package com.cn.dl.springbootdemo.controller;  import com.alibaba.fastjson.JSONObject; import com.cn.dl.springbootdemo.annotation.ApiVersion; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  /**  * @author yanshao  * @date 2022/8/11 10:37 上午  */ @RestController @ApiVersion("v2") @RequestMapping("/api/") public class StudentControllerV2 {      @GetMapping(value = "/query/studentInfo")     public JSONObject studentInfo(){         JSONObject result = new JSONObject();         result.put("name","yanshao");         result.put("age",27);         result.put("apiVersion","v2");         return result;     } } 

效果:

 不知道大家发现一个缺陷了吗?为了搞一个不同版本的接口,还得重新写一个类,是不是ApiVersion注解可以放在方法上?这需要修改一下ApiVersionHandlerMapping,就可以支持同一个Controller中不同版本的接口了。

package com.cn.dl.springbootdemo.handler;  import com.cn.dl.springbootdemo.annotation.ApiVersion; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition; import org.springframework.web.servlet.mvc.method.RequestMappingInfo; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;  import java.lang.reflect.Method; import java.util.Objects;  /**  * @author yanshao  * @date 2022/8/11 10:32 上午  */ public class ApiVersionHandlerMapping extends RequestMappingHandlerMapping {      @Override     protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mappingInfo) {         //类上加了ApiVersion注解 //        ApiVersion apiVersionClass = AnnotationUtils.findAnnotation(method.getDeclaringClass(), ApiVersion.class);          //方法上加了ApiVersion注解         ApiVersion apiVersionMethod = AnnotationUtils.findAnnotation(method, ApiVersion.class);          //可同时指定多个版本,类似于org.springframework.web.bind.annotation.RequestMapping.value的paths         String[] urlPatterns = Objects.isNull(apiVersionMethod) ? new String[0] : apiVersionMethod.value();          //api版本urlPatterns         PatternsRequestCondition apiVersionPattern = new PatternsRequestCondition(urlPatterns);          //当前未增加版本的paths,例如:/api/query/studentInfo //        PatternsRequestCondition curtPattern = mappingInfo.getPatternsCondition();          //加版本号增加到curtPath之前,例如:/v1 + /api/query/studentInfo -> /v1/api/query/studentInfo         PatternsRequestCondition updatedFinalPattern = apiVersionPattern.combine(mappingInfo.getPatternsCondition());          //构建新的RequestMappingInfo         mappingInfo = new RequestMappingInfo(                 mappingInfo.getName(),                 updatedFinalPattern,                 mappingInfo.getMethodsCondition(),                 mappingInfo.getParamsCondition(),                 mappingInfo.getHeadersCondition(),                 mappingInfo.getConsumesCondition(),                 mappingInfo.getProducesCondition(),                 mappingInfo.getCustomCondition()         );         super.registerHandlerMethod(handler, method, mappingInfo);     } } 
package com.cn.dl.springbootdemo.controller;  import com.alibaba.fastjson.JSONObject; import com.cn.dl.springbootdemo.annotation.ApiVersion; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;  /**  * @author yanshao  * @date 2022/8/11 10:37 上午  */ @RestController @RequestMapping("/api/") public class StudentController {      @ApiVersion("v1")     @GetMapping(value = "/query/studentInfo")     public JSONObject studentInfoV1(){         JSONObject result = new JSONObject();         result.put("name","yanshao");         result.put("age",27);         result.put("apiVersion","v1");         return result;     }      @ApiVersion("v2")     @GetMapping(value = "/query/studentInfo")     public JSONObject studentInfoV2(){         JSONObject result = new JSONObject();         result.put("name","yanshao");         result.put("age",27);         result.put("apiVersion","v2");         return result;     } } 

bg-shape bg-shape