springboot接口如何控制版本?

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;
    }
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

燕少༒江湖

给我一份鼓励!谢谢!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值