美文网首页
SpringBoot 2.x API 版本控制

SpringBoot 2.x API 版本控制

作者: BeRicher | 来源:发表于2018-12-24 12:45 被阅读0次

    之前有文章讲述在Spring MVC 中扩展 RequestMappingHandlerMapping 实现对版本的控制。
    但是在真正使用过程中不是很理想化,因为其需要替换掉WebMvcConfigurationSupport,替换后后,会将其提供的一系列默认组件全部移除。如我们注册拦截器使用的(RequestMappingHandlerAdapter)、全局异常拦截(ExceptionHandlerExceptionResolver)等。
    本文以Spring Boot 2.x为例,解决这个问题。

    配置WebMvcRegistrationsConfig

    @Configuration
    public class WebMvcRegistrationsConfig  implements WebMvcRegistrations {
        @Override
        public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
            return new ApiRequestMappingHandlerMapping();
        }
    }
    

    创建ApiRequestMappingHandlerMapping

    public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
        private static final String VERSION_FLAG = "{version}";
    
        private static RequestCondition<ApiVersionCondition> createCondition(Class<?> clazz) {
            RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);
            if (classRequestMapping == null) {
                return null;
            }
            StringBuilder mappingUrlBuilder = new StringBuilder();
            if (classRequestMapping.value().length > 0) {
                mappingUrlBuilder.append(classRequestMapping.value()[0]);
            }
            String mappingUrl = mappingUrlBuilder.toString();
            if (!mappingUrl.contains(VERSION_FLAG)) {
                return null;
            }
            ApiVersion apiVersion = clazz.getAnnotation(ApiVersion.class);
            return apiVersion == null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());
        }
    
        @Override
        protected RequestCondition<?> getCustomMethodCondition(Method method) {
            return createCondition(method.getClass());
        }
    
        @Override
        protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
            return createCondition(handlerType);
        }
    }
    

    @ApiVersion自定义注解

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ApiVersion {
        /**
         * @return 版本号
         */
        int value() default 1;
    }
    

    ApiVersionCondition请求映射条件

    RequestCondition相当针对请求与映射之间创建了判断条件。以此判断某个请求应落在哪个controller上。

    public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {
        private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile(".*v(\\d+).*");
    
        private int apiVersion;
    
        ApiVersionCondition(int apiVersion) {
            this.apiVersion = apiVersion;
        }
    
        private int getApiVersion() {
            return apiVersion;
        }
    
    
        @Override
        public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
            return new ApiVersionCondition(apiVersionCondition.getApiVersion());
        }
    
        @Override
        public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
            Matcher m = VERSION_PREFIX_PATTERN.matcher(httpServletRequest.getRequestURI());
            if (m.find()) {
                Integer version = Integer.valueOf(m.group(1));
                if (version >= this.apiVersion) {
                    return this;
                }
            }
            return null;
        }
    
        @Override
        public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
            return apiVersionCondition.getApiVersion() - this.apiVersion;
        }
    }
    

    使用

    V1Controller

    @RequestMapping("/{version}/version")
    @RestController
    public class V1Controller {
        @GetMapping
        public String test() {
            return "version1";
        }
        @GetMapping("/extend")
        public String extendTest() {
            return "extend";
        }
    }
    

    V2Controller

    @RequestMapping("/{version}/version")
    @RestController
    @ApiVersion(2)
    public class V2Controller {
       @GetMapping
        public String test() {
            return "version2";
        }
    }
    

    版本测试

    当请求http://localhost:8080/v1/version时会返回version1
    当请求http://localhost:8080/v2/version时返回version2
    实现了版本控制效果

    版本继承

    当请求http://localhost:8080/v2/version/extend时,会返回V1Controller中的extend。
    实现版本继承效果。

    版本分包

    当引入版本控制后,为了不让版本号污染我们的类名。
    我们一般不希望命名出现类似V1、V2的字眼。使用包的形式进行区分会更直观一些。如com.test.v1.UserController、com.test.v2.UserController。
    但此时spring以及扩展spring的一些组件(如mybatis)会抛出bean名称重复的错误。
    此时我们需要覆盖或手动指定spring的默认名称生成器AnnotationBeanNameGenerator,该类默认使用class的名称而非全类名作为bean的名称,故会出现名称冲突。抛出ConflictingBeanDefinitionException。

    Bean名称重复ConflictingBeanDefinitionException冲突解决

    Bean名称重复ConflictingBeanDefinitionException冲突解决

    Spring Boot 1.x

    Spring Boot 1.x中由于使用jdk1.8以下版本,没有default方法,故无法直接实现WebMvcRegistrations,其提供了WebMvcRegistrationsAdapter供我们继承。

        @Configuration
        public class WebMvcRegistrationsConfig extends WebMvcRegistrationsAdapter {
            @Override
            public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
                return new ApiRequestMappingHandlerMapping();
            }
        }
    

    快速开发框架
    高质量图片压缩工具

    相关文章

      网友评论

          本文标题:SpringBoot 2.x API 版本控制

          本文链接:https://www.haomeiwen.com/subject/qcoakqtx.html