参考方向
需求:
后端服务在提供api接口时,随着业务的变化,原有的接口很有可能不能满足现有的需求。在无法修改原有接口的情况下,只能提供一个新版本的接口来开放新的业务能力。
区分不同版本的api接口的方式有多种,其中一种简单通用的方式是在uri中添加版本的标识,例如/api/v1/user,api/v3/user。通过v+版本号来指定不同版本的api接口。在后端服务的代码中,可以将版本号直接写入代码中,例如,user接口提供两个入口方法,url mapping分别指定为/api/v1/user和/api/v2/user。
一、通过URI来获取版本号,进行拦截
1.创建注解@ApiVersion,代码如下
package tbcloud.admin.until;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: Dmm
* @date: 2019/4/11 19:45
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface ApiVersion {
// 定义接口的版本号
int value() default 1;
}
2.创建条件处理类ApiVersionRequestCondition,代码如下
package tbcloud.admin.until;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: Dmm
* @date: 2019/4/11 19:51
*/
public class ApiVersionRequestCondition implements RequestCondition<ApiVersionRequestCondition> {
// 用于匹配request中的版本号 v1 v2
private static final Pattern VERSION_PATTERN = Pattern.compile("/v(\\d+).*");
// 保存当前的版本号
private int version;
// 保存所有接口的最大版本号
private static int maxVersion = 1;
public ApiVersionRequestCondition(int version) {
this.version = version;
}
@Override
public ApiVersionRequestCondition combine(ApiVersionRequestCondition other) {
// 上文的getMappingForMethod方法中是使用 类的Condition.combine(方法的condition)的结果
// 确定一个方法的condition,所以偷懒的写法,直接返回参数的版本,可以保证方法优先,可以优化
// 在condition中增加一个来源于类或者方法的标识,以此判断,优先整合方法的condition
return new ApiVersionRequestCondition(other.version);
}
@Override
public ApiVersionRequestCondition getMatchingCondition(HttpServletRequest request) {
// 正则匹配请求的uri,看是否有版本号 v1
// Matcher matcher = VERSION_PATTERN.matcher(request.getRequestURI());
//
// System.out.println(request.getRequestURI()+"请求接口111111");
//
// if (matcher.find()) {
// String versionNo = matcher.group(1);
// int version = Integer.valueOf(versionNo);
// // 超过当前最大版本号或者低于最低的版本号均返回不匹配
// if (version <= maxVersion && version >= this.version) {
// return this;
// }
// }
// return null;
}
@Override
public int compareTo(ApiVersionRequestCondition other, HttpServletRequest request) {
// 以版本号大小判定优先级,越高越优先
return other.version - this.version;
}
public int getVersion() {
return version;
}
public static void setMaxVersion(int maxVersion) {
ApiVersionRequestCondition.maxVersion = maxVersion;
}
}
3.处理器ApiHandlerMapping
package tbcloud.admin.until;
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;
/**
* @author: Dmm
* @date: 2019/4/11 19:55
*/
public class ApiHandlerMapping extends RequestMappingHandlerMapping {
private int latestVersion = 1;
@Override
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
// 判断是否有@ApiVersion注解,构建基于@ApiVersion的RequestCondition
ApiVersionRequestCondition condition = buildFrom(AnnotationUtils.findAnnotation(handlerType, ApiVersion.class));
// 保存最大版本号
if (condition != null && condition.getVersion() > latestVersion) {
ApiVersionRequestCondition.setMaxVersion(condition.getVersion());
}
return condition;
}
@Override
protected RequestCondition<?> getCustomMethodCondition(Method method) {
// 判断是否有@ApiVersion注解,构建基于@ApiVersion的RequestCondition
ApiVersionRequestCondition condition = buildFrom(AnnotationUtils.findAnnotation(method, ApiVersion.class));
// 保存最大版本号
if (condition != null && condition.getVersion() > latestVersion) {
ApiVersionRequestCondition.setMaxVersion(condition.getVersion());
}
return condition;
}
private ApiVersionRequestCondition buildFrom(ApiVersion apiVersion) {
return apiVersion == null ? null : new ApiVersionRequestCondition(apiVersion.value());
}
}
4.配置拦截器WebConfig
package tbcloud.admin.web;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import tbcloud.admin.until.ApiHandlerMapping;
/**
* @author: Dmm
* @date: 2019/4/12 10:06
*/
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Override
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = new ApiHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());
return handlerMapping;
}
}
5.注意:需要把标签
<!--<mvc:annotation-driven/>-->
干掉。使用@Configuration,@Bean注册bean。
6.演示(controller层)
package tbcloud.admin.web;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import tbcloud.admin.until.ApiVersion;
import tbcloud.admin.until.User;
/**
* @author: Dmm
* @date: 2019/4/11 19:44
*/
@Controller
@RequestMapping("/api/test")
public class ApiVersionTest {
// @RequestMapping("/{version}/test/1")
// @ApiVersion
// @ResponseBody
// public String indexTo(){
//
// System.out.println("rfghnj");
//
// return "1";
// }
//
// @RequestMapping("/{version}/test/1")
// @ApiVersion(2)
// @ResponseBody
// public String indexTo2(){
//
// System.out.println("rfghnj--------------------");
//
// return "2";
// }
//
// @RequestMapping("/{version}/test/1")
// @ApiVersion(3)
// @ResponseBody
// public User indexTo3(){
//
//
// User user=new User();
// user.setUserName("2222");
//
// return user;
// }
//
// @RequestMapping("/{version}/test/1")
// @ApiVersion(4)
// @ResponseBody
// public User indexTo4(@RequestBody User user){
// return user;
// }
}
2.
3.
4.