Spring Boot API 版本权限控制

Spring Boot API 版本权限控制

之前有文章讲述在Spring MVC 中扩展 RequestMappingHandlerMapping 实现对版本的控制。
但是在真正使用过程中不是很理想化,因为其需要替换掉WebMvcConfigurationSupport,替换后后,会将其提供的一系列默认组件全部移除。如我们注册拦截器使用的(RequestMappingHandlerAdapter)、全局异常拦截(ExceptionHandlerExceptionResolver)等。
本文以Spring Boot 为例,解决这个问题。

##Spring Boot RequestMappingHandlerMapping

###自动配置时序图

Spring Boot API 版本权限控制_第1张图片

由上图则可以清晰看到,关键点在于是否存在WebMvcRegistrations。

进入WebMvcRegistrations发现其接口下提供了WebMvcRegistrationsAdapter转换器,可以轻松扩展我们所需的RequestMappingHandlerMapping,实现版本控制。

自动配置RequestMappingHandlerMapping源码分析

判断是否存在WebMvcConfigurationSupport

Spring Boot API 版本权限控制_第2张图片
Spring Boot 首先加载WebMvcAutoConfiguration
上图可以看到使用注解ConditionalOnMissingBean判断是否存在WebMvcConfigurationSupport,如果不存在,则该类不会注入,故如果我们直接继承WebMvcConfigurationSupport,则会导致整个全部配置失效。
** ps: SpringBoot的自动配置原理基本都是基于此类注解。**

引入WebMvc配置

Spring Boot API 版本权限控制_第3张图片

上图可看到通过@Import注解引入了EnableWebMvcConfiguration,点击进入该类可看到存在的构造方法中引入了WebMvcRegistrations。该类为WebMvcAutoConfiguration的内部类

Spring Boot API 版本权限控制_第4张图片

如何注入RequestMappingHandlerMapping

Spring Boot API 版本权限控制_第5张图片

由上图可发现EnableWebMvcConfiguration类中注入了RequestMappingHandlerMapping。其调用父类requestMappingHandlerMapping方法生成RequestMappingHandlerMapping。跟踪父类可发现:
Spring Boot API 版本权限控制_第6张图片

调用方法,创建了RequestMappingHandlerMapping,跟下去:

Spring Boot API 版本权限控制_第7张图片

发现其有子类,点击左侧箭头查看:

Spring Boot API 版本权限控制_第8张图片

发现回到了WebMvcAutoConfiguration中的EnableWebMvcConfiguration类。
其通过判断mvcRegistrations是否存在以及mvcRegistrations是否扩展RequestMappingHandlerMapping 来选择使用默认还是我们自定义的RequestMappingHandlerMapping 。

如何扩展

Spring Boot API 版本权限控制_第9张图片

创建一个类,继承WebMvcRegistrationsAdapter,重写getRequestMappingHandlerMapping,返回自定义RequestMappingHandlerMapping即可。
其类是WebMvcRegistrations方便对外扩展提供的转换器类,可以点进去看一下,默认全部返回null。

附上版本控制全部代码

WebMvcRegistrationsConfig


import com.sample.core.common.config.version.ApiRequestMappingHandlerMapping;
import org.springframework.boot.autoconfigure.web.WebMvcRegistrationsAdapter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

/**
 * Created by zhangbowen on 2017/7/8.
 */
@Configuration
public class WebMvcRegistrationsConfig extends WebMvcRegistrationsAdapter {
    @Override
    public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
        return new ApiRequestMappingHandlerMapping();
    }
}

ApiRequestMappingHandlerMapping


import org.springframework.core.annotation.AnnotationUtils;
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;

/**
 * Created by zhangbowen on 2017/7/8.
 */
public class ApiRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    @Override
    protected RequestCondition getCustomMethodCondition(Method method) {
        return createCondition(method.getClass());
    }

    @Override
    protected RequestCondition getCustomTypeCondition(Class handlerType) {
        return createCondition(handlerType);
    }
    private static RequestCondition createCondition(Class clazz) {
        RequestMapping classRequestMapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class);
        if (classRequestMapping == null) {
            return null;
        }
        StringBuilder mappingUrlBuilder = new StringBuilder();
        if (classRequestMapping.value().length > 0) {
            mappingUrlBuilder.append(classRequestMapping.value()[0]);
        }
        String mappingUrl = mappingUrlBuilder.toString();
        if (!mappingUrl.contains("{version}")) {
            return null;
        }
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(clazz, ApiVersion.class);
        return apiVersion == null ? new ApiVersionCondition(1) : new ApiVersionCondition(apiVersion.value());
    }
}

@ApiVersion

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by zhangbowen on 2017/4/15.
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ApiVersion {
    /**
     * 版本号
     * @return
     */
    int value();
}

ApiVersionCondition

import org.springframework.web.servlet.mvc.condition.RequestCondition;

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

/**
 * Created by zhangbowen on 2017/4/15.
 */
public class ApiVersionCondition implements RequestCondition {
    private final static Pattern VERSION_PREFIX_PATTERN = Pattern.compile("/v(\\d+).*");

    private int apiVersion;

    ApiVersionCondition(int apiVersion) {
        this.apiVersion = apiVersion;
    }

    public int getApiVersion() {
        return apiVersion;
    }


    @Override
    public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
        return new ApiVersionCondition(apiVersionCondition.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest httpServletRequest) {
        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;
    }

    @Override
    public int compareTo(ApiVersionCondition apiVersionCondition, HttpServletRequest httpServletRequest) {
        return apiVersionCondition.getApiVersion() - this.apiVersion;
    }
}

使用

VersionV1Controller

@RequestMapping("/{version}/version")
@RestController
public class VersionV1Controller {
    public String test() {
        return "version1";
    }
}

VersionV2Controller

@RequestMapping("/{version}/version")
@RestController
@ApiVersion(2)
public class VersionV2Controller {
    public String test() {
        return "version2";
    }
}

会不会有人发现这样的起名不怎么美观。。。下面这样分包会不会合理一些。
Spring Boot API 版本权限控制_第10张图片

修改一下Spring 默认的名称生成器就可以了

推荐开源脚手架:https://faster-framework.github.io/faster-framework-guide/
包含:前端接口、后台管理接口、后台管理web、代码生成器。

Spring MVC版本控制参考 http://www.cnblogs.com/jcli/p/springmvc_restful_version.html

腾讯云服务器促销149/年(1核2G1M带宽),需要的联系微信:zhangbowen002568。

你可能感兴趣的:(互联网)