接着上篇博客的代码继续写
1.接口版本控制
一个系统上线后会不断迭代更新,需求也会不断变化,有可能接口的参数也会发生变化,如果在原有的参数上直接修改,可能会影响线上系统的正常运行,这时我们就需要设置不同的版本,这样即使参数发生变化,由于老版本没有变化,因此不会影响上线系统的运行。
一般我们可以在地址上带上版本号,也可以在参数上带上版本号,还可以再 header 里带上版本号,这里我们在地址上带上版本号,大致的地址如:http://api.example.com/v1/test,其中,v1 即代表的是版本号。具体做法请看代码:
importorg.springframework.web.bind.annotation.Mapping;import java.lang.annotation.*;/*** 版本控制
*@authorXIHONGLEI
* @date 2018-11-15*/@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mappingpublic @interfaceApiVersion {intvalue();
}
importorg.springframework.web.servlet.mvc.condition.RequestCondition;importjavax.servlet.http.HttpServletRequest;importjava.util.regex.Matcher;importjava.util.regex.Pattern;/***@authorXIHONGLEI
* @date 2018-11-15*/
public class ApiVersionCondition implements RequestCondition{//路径中版本的前缀, 这里用 /v[1-9]/的形式
private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("v(\\d+)/");private intapiVersion;public ApiVersionCondition(intapiVersion) {this.apiVersion =apiVersion;
}
@OverridepublicApiVersionCondition combine(ApiVersionCondition other) {//采用最后定义优先原则,则方法上的定义覆盖类上面的定义
return newApiVersionCondition(other.getApiVersion());
}
@OverridepublicApiVersionCondition getMatchingCondition(HttpServletRequest request) {
Matcher m=VERSION_PREFIX_PATTERN.matcher(request.getRequestURI());if(m.find()) {
Integer version= Integer.valueOf(m.group(1));if (version >= this.apiVersion) {return this;
}
}return null;
}
@Overridepublic intcompareTo(ApiVersionCondition other, HttpServletRequest request) {//优先匹配最新的版本号
return other.getApiVersion() - this.apiVersion;
}public intgetApiVersion() {returnapiVersion;
}
}
importorg.springframework.core.annotation.AnnotationUtils;importorg.springframework.web.servlet.mvc.condition.RequestCondition;importorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;importjava.lang.reflect.Method;/***@authorXIHONGLEI
* @date 2018-11-15*/
public class CustomRequestMappingHandlerMapping extendsRequestMappingHandlerMapping {
@Overrideprotected RequestCondition getCustomTypeCondition(Class>handlerType) {
ApiVersion apiVersion= AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);returncreateCondition(apiVersion);
}
@Overrideprotected RequestConditiongetCustomMethodCondition(Method method) {
ApiVersion apiVersion= AnnotationUtils.findAnnotation(method, ApiVersion.class);returncreateCondition(apiVersion);
}private RequestConditioncreateCondition(ApiVersion apiVersion) {return apiVersion == null ? null : newApiVersionCondition(apiVersion.value());
}
}
#然后在WebConfig配置类中注入Beanimportcom.hello.config.CustomRequestMappingHandlerMapping;importcom.hello.filter.ApiInterceptor;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.boot.SpringBootConfiguration;importorg.springframework.context.annotation.Bean;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importorg.springframework.web.servlet.config.annotation.InterceptorRegistry;importorg.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;importorg.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;/*** 配置类
*@authorXIHONGLEI
* @date 2018-10-31*/@SpringBootConfigurationpublic class WebConfig extendsWebMvcConfigurationSupport {
@Overrideprotected voidaddInterceptors(InterceptorRegistry registry) {super.addInterceptors(registry);//将 ApiInterceptor 拦截器类添加进去
registry.addInterceptor(newApiInterceptor());
}
@Override
@BeanpublicRequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping= newCustomRequestMappingHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors());returnhandlerMapping;
}
}
#最后定义一个带版本控制的接口importcom.hello.WebConfig;importcom.hello.config.ApiVersion;importcom.hello.entity.ContractDetailDto;importcom.hello.service.CheckPositionService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.ResponseBody;importorg.springframework.web.bind.annotation.RestController;importjava.util.List;
@RestControllerpublic classHelloController {
@AutowiredprivateWebConfig webConfig;
@ApiVersion(1)
@RequestMapping("{version}/getName")publicString vGetName() {return "Hello World! version 1";
}
@ApiVersion(2)
@RequestMapping("{version}/getName")publicString getName() {return "Hello World! version 2";
}
}
查看效果:
2.模板引擎
在传统的 SpringMVC 架构中,我们一般将 JSP、HTML 页面放到 webapps 目录下面,但是 Spring Boot 没有 webapps,更没有 web.xml,如果我们要写界面的话,该如何做呢?
Spring Boot 官方提供了几种模板引擎:FreeMarker、Velocity、Thymeleaf、Groovy、mustache、JSP。
这里以 FreeMarker 为例讲解 Spring Boot 的使用。
首先引入 FreeMarker 依赖:
org.springframework.boot
spring-boot-starter-freemarker
在 resources 下面建立两个目录:static 和 templates,如图所示:
其中 static 目录用于存放静态资源,譬如:CSS、JS、HTML 等,templates 目录存放模板引擎文件,我们可以在 templates 下面创建一个文件:index.ftl(freemarker 默认后缀为 .ftl),并添加内容:
Hello ${name}!