springboot-接口版本区分

         在进行REST接口的开发中,如果项目不断的进行迭代开发,需求不断的变化,会出现不同的版本,一个接口版本1和版本2的业务逻辑可能完全不同,但是又需要兼容之前的版本,我们可能不能在之前的接口进行修改,只能重新另外一个版本的接口,那该如何实现了?目前有几种方法,常见的有:一种是在url中加入版本号,第二种是在请求头中加入版本号。下面我给出一个小demo,基于在请求的url中加入版本号,扩展可以根据自己的需要。

 

一,新建springboot项目

新建一个springboot项目,pom.xml的配置如下:



	4.0.0

	com.jack
	springboot_version
	0.0.1-SNAPSHOT
	jar

	springboot_version
	Demo project for Spring Boot

	
		org.springframework.boot
		spring-boot-starter-parent
		2.0.4.RELEASE
		 
	

	
		UTF-8
		UTF-8
		1.8
	

	
		
			org.springframework.boot
			spring-boot-starter-web
		

		
			org.springframework.boot
			spring-boot-starter-test
			test
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	



 

配置文件如下:

server:
  port: 9090

 

二,实现自定义版本控制的代码

1,自定义版本控制的注解

package com.jack.springboot_version.annotation;

import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:44
 * @Description:
 *版本控制注解
 */
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
    /**
     * 标识版本号
     * @return
     */
    int value();
}

2,自定义url匹配逻辑

package com.jack.springboot_version.config;

import org.springframework.lang.Nullable;
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:57
 * @Description:
 */
public class ApiVersionCondition implements RequestCondition {
    // 路径中版本的前缀, 这里用 /v[1-9]/的形式
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");

    /**
     * api的版本
     */
    private int apiVersion;

    public ApiVersionCondition(int apiVersion) {
        this.apiVersion = apiVersion;
    }


    /**
     * 将不同的筛选条件合并
     * @param apiVersionCondition
     * @return
     */
    @Override
    public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
        //return null;
        // 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
        return new ApiVersionCondition(apiVersionCondition.getApiVersion());
    }


    /**
     * 根据request查找匹配到的筛选条件
     * @param httpServletRequest
     * @return
     */
    @Nullable
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
        //return null;
        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;
    }

    /**
     * 不同筛选条件比较,用于排序
     * @param apiVersionCondition
     * @param httpServletRequest
     * @return
     */
    @Override
    public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
        //return 0;
        // 优先匹配最新的版本号
        return apiVersionCondition.getApiVersion() - this.apiVersion;
    }

    public int getApiVersion() {
        return apiVersion;
    }


}

 

3,自定义匹配的处理器

package com.jack.springboot_version.config;

import com.jack.springboot_version.annotation.ApiVersion;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.lang.reflect.Method;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:49
 * @Description:
 * 重写RequestMappingHandlerMapping类的一些方法
 */
public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition getCustomTypeCondition(Class handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return createCondition(apiVersion);
    }

    @Override
    protected RequestCondition getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return createCondition(apiVersion);
    }

    private RequestCondition createCondition(ApiVersion apiVersion) {
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}

 

4,自定义WebMvcConfigurationSupport

  核心代码如下:

package com.jack.springboot_version.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:50
 * @Description:
 */
@SpringBootConfiguration
public class WebConfig extends WebMvcConfigurationSupport {

    /**
     * 重写请求过处理的方法
     * @return
     */
    @Override
    public RequestMappingHandlerMapping requestMappingHandlerMapping() {
        //return super.requestMappingHandlerMapping();
        RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
        handlerMapping.setOrder(0);
        return handlerMapping;
    }
}

 

三,编写测试的控制器

1,版本1的控制器:

package com.jack.springboot_version.controller.v1;

import com.jack.springboot_version.annotation.ApiVersion;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:52
 * @Description:
 */
@ApiVersion(1)
@RestController
@RequestMapping("{version}/hello")
public class Hello1Controller {

    @RequestMapping("/world")
    public String helloWorld(){
        System.out.println("版本是1的接口");
        return "hello,world .version is 1";
    }

}

 

2,版本2的控制器:

package com.jack.springboot_version.controller.v2;

import com.jack.springboot_version.annotation.ApiVersion;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * create by jack 2018/8/19
 *
 * @auther jack
 * @date: 2018/8/19 10:52
 * @Description:
 */
@ApiVersion(2)
@RestController
@RequestMapping("{version}/hello")
public class Hello2Controller {

    @RequestMapping("/world")
    public String helloWorld(){
        System.out.println("版本是2的接口");
        return "hello,world .version is 2";
    }

}

 

四,测试demo

使用postman进行测试:

1,测试版本1,:

测试url:http://localhost:9090/v1/hello/world

测试结果:

springboot-接口版本区分_第1张图片

 

 

2,测试版本2,:

测试url:http://localhost:9090/v2/hello/world

测试结果:

springboot-接口版本区分_第2张图片

 

   源码地址

总结:通过自定义springmvc的url匹配规则,实现接口的版本控制,url增加了版本号,如果不存在高版本的版本接口则匹配代码中版本号最高的处理逻辑。使用版本号对我们项目的接口的迭代开发提供了方便。 

 

 

 

你可能感兴趣的:(spring,boot)