RequestCondition详解及详细案例

RequestCondition详解

1.为什么要对API进行版本管理

​ 因为软件有一个不断升级迭代的过程,而在升级中我们业务需求可能不断在更新,所以我们需要对以前的API接口不断的更新以满足变化的需求,但是在更新升级的过程中我们势必又要保证原来功能的可用性,所以我们就必须要对同一个API接口进行多版本的管理。

2.RequestCondition 介绍

主要案例:

  • 版本切换
  • 灰度发布

​ 在 spring mvc 中,通过DispatchServlet接收客户端发起的一个请求之后,会通过 HanderMapping 来获取对应的请求处理器;而 HanderMapping 如何找到可以处理这个请求的处理器呢,这就需要 RequestCondition 来决定了。

接口定义如下,主要有三个方法:

public interface RequestCondition<T> {
  // 一个http接口上有多个条件规则时,用于合并
  T combine(T other);
  // 这个是重点,用于判断当前匹配条件和请求是否匹配;如果不匹配返回null
  // 如果匹配,生成一个新的请求匹配条件,该新的请求匹配条件是当前请求匹配条件针对指定请求request的剪裁
  // 举个例子来讲,如果当前请求匹配条件是一个路径匹配条件,包含多个路径匹配模板,
  // 并且其中有些模板和指定请求request匹配,那么返回的新建的请求匹配条件将仅仅
  // 包含和指定请求request匹配的那些路径模板。
  @Nullable
  T getMatchingCondition(HttpServletRequest request);
  // 针对指定的请求对象request发现有多个满足条件的,用来排序指定优先级,使用最优的进行响应
    // 针对指定的请求对象request比较两个请求匹配条件。
    // 该方法假定被比较的两个请求匹配条件都是针对该请求对象request调用了
    // #getMatchingCondition方法得到的,这样才能确保对它们的比较
    // 是针对同一个请求对象request,这样的比较才有意义(最终用来确定谁是
    // 更匹配的条件)。
  int compareTo(T other, HttpServletRequest request);
}
  • combine: 某个接口有多个规则时,进行合并 - 比如类上指定了@RequestMapping的 url 为 root - 而方法上指定的@RequestMapping的 url 为 method - 那么在获取这个接口的 url 匹配规则时,类上扫描一次,方法上扫描一次,这个时候就需要把这两个合并成一个,表示这个接口匹配root/method

  • getMatchingCondition: - 判断是否成功,失败返回 null;否则,则返回匹配成功的条件

  • compareTo: - 多个都满足条件时,用来指定具体选择哪一个

由接口源代码可以看出,接口RequestCondition是一个泛型接口。事实上,它的泛型参数T通常也会是一个RequestCondition对象。

3.详细例子:

​ 一个接口出现 url 相同且请求方法也相同的,这样的情况通常发生在接口迭代过程中,如果出现问题,可以无感切到原接口。所以可以利用RequestCondition来做接口的版本控制。

3.1.定义@ApiVersion注解:

package com.kami.mall.config;
import java.lang.annotation.*;

/**
 * @Auther xzh
 * @Date 2023-11-23 15:48
 */
@Target(value = ElementType.METHOD)
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {
    String value();
}

3.2.定义匹配规则

继承自RequestCondition类,如下ApiVersionCondition类:

package com.kami.mall.config;
import com.kami.mall.entity.DO.User;
import com.kami.mall.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.condition.RequestCondition;
import javax.servlet.http.HttpServletRequest;

/**
 * @Auther xzh
 * @Date 2023-11-23 15:54
 */
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {

    private static ApiVersionMapper apiVersionMapper;

    public static void setUserMapper(ApiVersionMapper apiVersionMapper) {
        ApiVersionCondition.apiVersionMapper = apiVersionMapper;
    }

    public static ApiVersionMapper getApiVersionMapper() {
        return apiVersionMapper;
    }

    String apiVersion;

    public String getApiVersion() {
        return apiVersion;
    }

    public void setApiVersion(String apiVersion) {
        this.apiVersion = apiVersion;
    }

    public ApiVersionCondition(String apiVersion){
        this.apiVersion = apiVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition apiVersionCondition) {
        System.out.println("combine ==apiVersionCondition.getApiVersion()=>"+apiVersionCondition.getApiVersion());
        return new ApiVersionCondition(apiVersionCondition.getApiVersion());
    }

    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        ApiVersion apiVersion = apiVersionMapper.getApiVersion(this.apiVersion);
        String requestURI = request.getRequestURI();
        System.out.println("getMatchingCondition ==requestURI=>"+requestURI);

        if(apiVersion.getApiVersion.compareTo(this.apiVersion)>=0){
            return this;
        }
        return null;
    }

    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest httpServletRequest) {
        System.out.println("compareTo ==other.getApiVersion()=>"+other.getApiVersion());
        System.out.println("compareTo ==this.apiVersion=>"+this.apiVersion);
        System.out.println(other.getApiVersion().compareTo(this.apiVersion));
        return other.getApiVersion().compareTo(this.apiVersion);
    }
}

3.3.注册到 HandlerMapping 上

匹配规则指定完毕之后,需要注册到 HandlerMapping 上才能生效,如下ApiVersionHandleMapping映射器:

package com.kami.mall.config;
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;

/**
 * 支持使用多版本的控制器
 * @Auther xzh
 * @Date 2023-11-23 16:16
 */
public class ApiVersionHandleMapping extends RequestMappingHandlerMapping {

    public ApiVersionHandleMapping(){
        System.out.println("====ApiVersionHandleMapping===========init========");
    }

    /**
     * 容器初始化执行
     * 所有controller都会使用该方法
     * @param handlerType
     * @return
     */
    @Override
    protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(handlerType, ApiVersion.class);
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }

    /**
     * 容器初始化时执行
     * @param method
     * @return
     */
    @Override
    protected RequestCondition<?> getCustomMethodCondition(Method method) {
        ApiVersion apiVersion = AnnotationUtils.findAnnotation(method, ApiVersion.class);
        return apiVersion == null ? null : new ApiVersionCondition(apiVersion.value());
    }
}

3.4.注册到 Spring MVC 容器中

最后则是需要将我们的 HandlerMapping 注册到 Spring MVC 容器,在这里我们借助WebMvcConfigurationSupport来手动注册,如下ApiVersionMappingRegister类:

package com.kami.mall.config;

import com.kami.mall.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcRegistrations;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import javax.annotation.PostConstruct;

/**
 * @Auther xzh
 * @Date 2023-11-23 16:21
 */
@Configuration
public class ApiVersionMappingRegister implements WebMvcRegistrations {

    @Autowired
    private ApiversionMapper apiversionMapper;

    public RequestMappingHandlerMapping getRequestMappingHandlerMapping(){
        ApiVersionHandleMapping handleMapping = new ApiVersionHandleMapping();
//        handleMapping.setOrder(0);
        return handleMapping;
    }

    @PostConstruct
    public void initMethod(){
        ApiVersionCondition.setApiversionMapper(apiversionMapper);
    }
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值