Web 接口版本控制

236 阅读1分钟

传统Spring  Mvc配置和Spring boot类似。

  • 定义版本注解
import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface ApiVersion {
	/**
	 * 版本号
	 * @return
	 */
	int value() default 1;
}
  • 版本uri解析规则
import org.springframework.web.servlet.mvc.condition.RequestCondition;

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

public class ApiVesrsionCondition implements RequestCondition<ApiVesrsionCondition> {

	// 路径中版本的前缀, 这里用 /v[1-9]/的形式
	private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");

	private int apiVersion;

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

	@Override
	public ApiVesrsionCondition combine(ApiVesrsionCondition other) {
		// 采用最后定义优先原则,则方法上的定义覆盖类上面的定义
		return new ApiVesrsionCondition(other.getApiVersion());
	}

	@Override
	public int compareTo(ApiVesrsionCondition other, HttpServletRequest request) {
		// 优先匹配最新的版本号
		return other.getApiVersion() - this.apiVersion;
	}

	public int getApiVersion() {
		return apiVersion;
	}

	@Override
	public ApiVesrsionCondition getMatchingCondition(HttpServletRequest request) {
		Matcher m = VERSION_PREFIX_PATTERN.matcher(request.getServletPath());
		if (m.find()) {
			Integer version = Integer.valueOf(m.group(1));
			if (version >= this.apiVersion)
				return this;
		} else {
			return this;
		}
		return null;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + apiVersion;
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ApiVesrsionCondition other = (ApiVesrsionCondition) obj;
		if (apiVersion != other.apiVersion)
			return false;
		return true;
	}

}
  • 继承重写请求处理
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;

public class CustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping {

	@Override
	protected RequestCondition<ApiVesrsionCondition> getCustomTypeCondition(Class<?> handlerType) {
		ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
		return createCondition(apiVersion);
	}

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

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

}
  • 版本控制使用
@RestController
@RequestMapping("api/{version}/test")
public class TestRest1 {

    @ApiVersion(1)
    @GetMapping
    public String test1() {

        return "Ok";
    }


    @ApiVersion(2)
    @GetMapping
    public String test2() {

        return "Ok";
    }
}

@RestController
@RequestMapping("api")
public class TestRest1 {

    @ApiVersion(1)
    @GetMapping("{version}/test")
    public String test1() {

        return "Ok";
    }


    @ApiVersion(2)
    @GetMapping("{version}/test")
    public String test2() {

        return "Ok";
    }
}

集成配置

  • Spring Boot 配置

public class DefaultMvcConfig extends WebMvcConfigurationSupport {

@Override
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
	RequestMappingHandlerMapping handlerMapping = new CustomRequestMappingHandlerMapping();
	handlerMapping.setOrder(0);
	handlerMapping.setInterceptors(getInterceptors());
	return handlerMapping;
}

// ...

}

  • 传统Spring Mvc项目配置
// spring-mvc.xml,增加配置
<bean class="com.xxx.CustomRequestMappingHandlerMapping">
	<property name="order" value="0" />
</bean>