通过自定义RequestMappingHandlerMapping实现接口版本控制

1.定义VersionRequestMappingHandlerMapping

/**
 * 客户端在传入Accept 参数添加当前请求的api版本,实现客户端版本平滑升级及向后的兼容
 * <p>
 * 使用:
 * 配置CustomRequestMappingHandlerMapping替换默认的DefaultAnnotationHandlerMapping
 * handlerAdapter也需要作调整org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
 * <p>
 * 在使用fastjson作为messageConverter时需要添加application/*+json;charset=UTF-8作为支持的mediaType
 *
 */
public class VersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        VersionedResource typeAnnotation = AnnotationUtils.findAnnotation(handlerType, VersionedResource.class);
        return createCondition(typeAnnotation);
    }

    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        VersionedResource methodAnnotation = AnnotationUtils.findAnnotation(method, VersionedResource.class);
        return createCondition(methodAnnotation);
    }

    private RequestCondition<?> createCondition(VersionedResource versionMapping) {
        if (versionMapping != null) {
            return new VersionedResourceRequestCondition( versionMapping.from(), versionMapping.to());
        }
        return null;
    }
}

2.定义VersionedResourceRequestCondition

public class VersionedResourceRequestCondition extends AbstractRequestCondition<VersionedResourceRequestCondition> {
    private Logger logger = LoggerFactory.getLogger(VersionedResourceRequestCondition.class);
    private final Set<VersionRange> versions;
    private Pattern regexPattern = Pattern.compile("(\\d+\\.\\d+)");

    public VersionedResourceRequestCondition(String from, String to) {
        this(versionRange(from, to));
    }

    public VersionedResourceRequestCondition(Collection<VersionRange> versions) {
        this.versions = Collections.unmodifiableSet(new HashSet<>(versions));
    }

    private static Set<VersionRange> versionRange(String from, String to) {
        Set<VersionRange> versionRanges = new HashSet<>();
        if (StringUtils.hasText(from)) {
            String toVersion = (StringUtils.hasText(to) ? to : Version.MAX_VERSION);
            VersionRange versionRange = new VersionRange(from, toVersion);
            versionRanges.add(versionRange);
        }
        return versionRanges;
    }

    @Override
    protected Collection<?> getContent() {
        return versions;
    }

    @Override
    protected String getToStringInfix() {
        return " && ";
    }

    @Override
    public VersionedResourceRequestCondition combine(VersionedResourceRequestCondition other) {
        logger.debug("Combining:\n{}\n{}", this, other);
        Set<VersionRange> newVersions = new LinkedHashSet<VersionRange>(this.versions);
        newVersions.addAll(other.versions);
        return new VersionedResourceRequestCondition(newVersions);
    }

    @Override
    public VersionedResourceRequestCondition getMatchingCondition(HttpServletRequest request) {
        String accept = request.getHeader("api_version");
        if (StringUtils.hasText(accept)) {
            Matcher matcher = regexPattern.matcher(accept);
            if (matcher.matches()) {
                String version = matcher.group(1);
                logger.debug("Version={}", version);
                AntPathMatcher antPathMatcher = new AntPathMatcher();
                for (VersionRange versionRange : versions) {
                    if (versionRange.includes(version)) {
                        return this;
                    }
                }
            }
        }
        logger.warn("original url:[{}] with header api_version:[{}],handler versions:[{}],Didn't find matching version", request.getRequestURI(), accept, versions);
        return null;
    }


    @Override
    public int compareTo(VersionedResourceRequestCondition other, HttpServletRequest request) {
        return 0;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("version={");
        for (VersionRange range : versions) {
            sb.append(range).append(",");
        }
        sb.append("}");

        return sb.toString();
    }
}

3.定义VersionRange

public class VersionRange {
    private Version from;
    private Version to;

    public VersionRange(String from, String to) {
        this.from = new Version(from);
        this.to = new Version(to);
    }

    public boolean includes(String other) {
        Version otherVersion = new Version(other);
        if (from.compareTo(otherVersion) <= 0 && to.compareTo(otherVersion) >= 0) {
            return true;
        }
        return false;
    }

    @Override
    public String toString() {
        return "range[" + from + "-" + to + "]";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        VersionRange that = (VersionRange) o;
        return from.equals(that.from) && to.equals(that.to);
    }

    @Override
    public int hashCode() {
        return Objects.hash(from, to);
    }
}

4.定义Version

public class Version implements Comparable<Version> {
    public static final String MAX_VERSION = "99.99";
    private static final int VERSION_LENGTH = 2;
    private final int major;
    private final int minor;

    public Version(String version) {
        String[] tokens = version.split("\\.");

        if (tokens.length != VERSION_LENGTH) {
            throw new IllegalArgumentException("Invalid version " + version + ". The version must have major and minor number.");
        }

        major = Integer.parseInt(tokens[0]);
        minor = Integer.parseInt(tokens[1]);
    }

    @Override
    public int compareTo(Version o) {
        if (this.major > o.major) {
            return 1;
        } else if (this.major < o.major) {
            return -1;
        } else if (this.minor > o.minor) {
            return 1;
        } else if (this.minor < o.minor) {
            return -1;
        } else {
            return 0;
        }
    }

    @Override
    public String toString() {
        return "v" + major + "." + minor;
    }
}

5.定义版本号注解

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface VersionedResource {

    String from() default "";

    String to() default Version.MAX_VERSION;
}

6.在配置类中注入VersionRequestMappingHandlerMapping

 /**
     * 注入自定义的RequestMappingHandlerMapping
     */
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        VersionRequestMappingHandlerMapping vrmhm = new VersionRequestMappingHandlerMapping();
        vrmhm.setOrder(0);
        return vrmhm;
    }

7.在controller中应用@VersionedResource注解

 @VersionedResource(from = "1.0")
 @PostMapping("/insert")
 public ApiResponse<?> insert(@RequestBody @Valid WebRequestDTO request) {
 // 处理业务逻辑
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值