springboot 接口版本区分方式

springboot 接口版本区分

在进行REST接口的开发中,如果项目不断的进行迭代开发,需求不断的变化,会出现不同的版本,一个接口版本1和版本2的业务逻辑可能完全不同,但是又需要兼容之前的版本,我们可能不能在之前的接口进行修改,只能重新另外一个版本的接口,那该如何实现了?

目前有几种方法,常见的有:一种是在url中加入版本号,第二种是在请求头中加入版本号。

下面我给出一个小demo,基于在请求的url中加入版本号,扩展可以根据自己的需要。

一、新建springboot项目

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

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.jack</groupId>

<artifactId>springboot_version</artifactId>

<version>0.0.1-SNAPSHOT</version>

<packaging>jar</packaging>

<name>springboot_version</name>

<description>Demo project for Spring Boot</description>

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>2.0.4.RELEASE</version>

<relativePath/> <!-- lookup parent from repository -->

</parent>

<properties>

<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

<java.version>1.8</java.version>

</properties>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

</dependencies>

<build>

<plugins>

<plugin>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-maven-plugin</artifactId>

</plugin>

</plugins>

</build>

</project>

配置文件如下:

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<ApiVersionCondition> {

// 路径中版本的前缀, 这里用 /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<ApiVersionCondition> getCustomTypeCondition(Class<?> handlerType) {

ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);

return createCondition(apiVersion);

}

@Override

protected RequestCondition<ApiVersionCondition> getCustomMethodCondition(Method method) {

ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);

return createCondition(apiVersion);

}

private RequestCondition<ApiVersionCondition> 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

测试结果:

2,测试版本2,:

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

测试结果:

git:https://github.com/wj903829182/springcloud5/tree/master/springboot_version

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

springboot 两个版本的差异

背景:前几天被人问到了SpringBoot 使用的是哪个版本的?两个版本的差异?完全Hold不住,今天记起来去稍微了解下。

如今市面上就有SpringBoot2.X.X 和SpringBoot1.X.X 两个新旧大版本。其中,SpringBoot1和SpringBoot2主要区别有如下两个方面(MVC部分):

一、WebMvcConfigurerAdapter

WebMvcConfigurerAdapter该抽象类在新版的SpringBoot中有改动,部分方法过时。由于SpringBoot的2.0 及其以上版本最低已支持Java1.8,而Java1.8中有个defualt关键字的新特性,于是SpringBoot 2.0.0 对WebMvcConfigurerAdapter该抽象类的上层接口WebMvcConfigurer进行了改造,将WebMvcConfigurer中的方法全部改为使用default关键字修饰;因此,SpringBoot2版本在使用WebMvcConfigurerAdapter抽象类时不需要再使用适配器进行适配。

WebMvcConfigurer部分代码如下:

public interface WebMvcConfigurer {

/**

* Helps with configuring HandlerMappings path matching options such as trailing slash match,

* suffix registration, path matcher and path helper.

* Configured path matcher and path helper instances are shared for:

* <ul>

* <li>RequestMappings</li>

* <li>ViewControllerMappings</li>

* <li>ResourcesMappings</li>

* </ul>

* @since 4.0.3

*/

void configurePathMatch(PathMatchConfigurer configurer);

/**

* Configure content negotiation options.

*/

void configureContentNegotiation(ContentNegotiationConfigurer configurer);

/**

* Configure asynchronous request handling options.

*/

void configureAsyncSupport(AsyncSupportConfigurer configurer);

/**

* Configure a handler to delegate unhandled requests by forwarding to the

* Servlet container's "default" servlet. A common use case for this is when

* the {@link DispatcherServlet} is mapped to "/" thus overriding the

* Servlet container's default handling of static resources.

*/

void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);

/**

* Add {@link Converter}s and {@link Formatter}s in addition to the ones

* registered by default.

*/

void addFormatters(FormatterRegistry registry);

更直接的说,就是WebMvcConfigurerAdapter 被WebMvcConfigurer 接口替代了,可以直接通过继承WebMvcConfigurer接口,然后实现它的defalut方法来使用WebMvcConfigurerAdapter。

除此之外,WebMvcConfigurerAdapter 还可以用 WebMvcConfigurationSupport 替代,只不过使用WebMvcConfigurationSupport这个类来替换WebMvcConfigurerAdapter时会全面接管对SpringMVC的配置,即SpringBoot对SpringMVC的自动配置全部失效,均使用用户对SpringMVC的配置。

二、SpringMVC拦截器拦截静态资源

SpringBoot1旧版本中配置的拦截器对静态资源默认是放行不拦截对,而在SpringBoot 2.0.0及其以上版本的拦截器不会对静态资源默认放行,同样也会进行拦截。此时,就需要为使用到的静态资源排除排除其请求路径,这样在使用SpringBoot2新版本时拦截器才不会拦截静态资源。

排除拦截静态资源示例如下:

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new LoginHandlerIntercepter()).addPathPatterns("/**") .excludePathPatterns("/asserts/**","/webjars/**"); }

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

以上是 springboot 接口版本区分方式 的全部内容, 来源链接: utcz.com/p/249732.html

回到顶部