美文网首页
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