1 应用场景
1、接口版本迭代过程中
/rest/market/general 第一版:提供基础数据 用户:我不想升级
/rest/market/general 第二版:提供基础数据+补充数据 用户:升级程序
2、对外提供接口
服务中只提供一个url /rest/market/general
根据不同的约定方式去取数据
比如: 在header中携带不同的参数访问不同的接口
2 解决方法
- 保留原有的url映射绑定
- 增加新的绑定方式
- 修改接口映射的条件
3、自定义注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiSign {
/**
* 这几个参数不是关键,根据自己条件进行自定义
* @return
*/
String value();
String headerKey() default "headerKey";
String paramKey() default "paramKey";
}
4、定制每次请求的匹配条件
import org.springframework.beans.BeanUtils;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;
/**
* api请求条件判断
*/
public class APIRequestCondition implements RequestCondition<APIRequestCondition> {
private String paramKey;
private String headerKey;
private String apiName;
public APIRequestCondition() {
}
public APIRequestCondition(String paramKey, String headerKey, String apiName) {
this.paramKey = paramKey;
this.headerKey = headerKey;
this.apiName = apiName;
}
/**
* 将类上的注解和方法上的注解进行合并
*
* @param other
* @return
*/
@Override
public APIRequestCondition combine(APIRequestCondition other) {
APIRequestCondition requestCondition = new APIRequestCondition();
BeanUtils.copyProperties(other, requestCondition);
return requestCondition;
}
/**
* 请求过程的匹配规则
*
* @param request
* @return
*/
@Override
public APIRequestCondition getMatchingCondition(HttpServletRequest request) {
String apiHeader = request.getHeader(headerKey);
if(apiName.equals(apiHeader)) {
return this;
}
return null;
}
@Override
public int compareTo(APIRequestCondition other, HttpServletRequest request) {
return 0;
}
/**
* 校验请求头格式
*
* @param request
* @return
*/
private String checkRequestHeader(HttpServletRequest request) {
return request.getHeader(headerKey);
}
}
5、增加自定义的接口匹配规则
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.lang.reflect.Method;
/**
* 请求映射,把方法关联到自定义的apiRequestCondition
*/
public class ApiHandlerMapping extends RequestMappingHandlerMapping {
/**
* 锁定扫描条件
* @param beanType
* @return
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
/**
* 管理定制的在类上访问条件
* @param handlerType
* @return
*/
@Override
protected RequestCondition<APIRequestCondition> getCustomTypeCondition(Class<?> handlerType) {
ApiSign annotation = AnnotationUtils.findAnnotation(handlerType, ApiSign.class);
return createCondition(annotation);
}
/**
* 管理自己在方法上的访问条件
* @param method
* @return
*/
@Override
protected RequestCondition<APIRequestCondition> getCustomMethodCondition(Method method) {
ApiSign apiName = AnnotationUtils.findAnnotation(method, ApiSign.class);
return createCondition(apiName);
}
/**
* 初始化自定义的访问条件
* @param apiName
* @return
*/
private RequestCondition<APIRequestCondition> createCondition(ApiSign apiName) {
return apiName == null ? null : new APIRequestCondition(apiName.paramKey(), apiName.headerKey(), apiName.value());
}
}
6、容器加载过程注入接口匹配规则
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
@Configuration
public class RequestMappingConfig
implements WebMvcRegistrations
{
@Override
public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
return new ApiHandlerMapping();
}
}
7、 测试
import com.test.demo.Api.ApiSign;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rest/market/general")
public class TestController {
@GetMapping("/base")
@ApiSign("test")
public String getData(){
return "不想升级:基础数据";
}
@GetMapping("/base")
@ApiSign("vip")
public String getNewData(){
return "升级:基础数据 + 补充数据";
}
@GetMapping("/base")
public String getNullData(){
return "can not see";
}
}
测试结果:
接口header带 test :显示 不想升级:基础数据
接口header带 vip:升级:基础数据 + 补充数据