欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringBoot實(shí)現(xiàn)API接口多版本支持的示例代碼

 更新時(shí)間:2020年10月26日 11:17:05   作者:Atomic  
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)API接口多版本支持的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

一、簡(jiǎn)介

產(chǎn)品迭代過(guò)程中,同一個(gè)接口可能同時(shí)存在多個(gè)版本,不同版本的接口URL、參數(shù)相同,可能就是內(nèi)部邏輯不同。尤其是在同一接口需要同時(shí)支持舊版本和新版本的情況下,比如APP發(fā)布新版本了,有的用戶可能不選擇升級(jí),這是后接口的版本管理就十分必要了,根據(jù)APP的版本就可以提供不同版本的接口。

二、代碼實(shí)現(xiàn)

本文的代碼實(shí)現(xiàn)基于SpringBoot 2.3.4-release

1.定義注解

ApiVersion

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiVersion {

  /**
   * 版本。x.y.z格式
   *
   * @return
   */
  String value() default "1.0.0";
}

value值默認(rèn)為1.0.0

EnableApiVersion

/**
 * 是否開(kāi)啟API版本控制
 */
@Target(ElementType.TYPE)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Import(ApiAutoConfiguration.class)
public @interface EnableApiVersion {
}

在啟動(dòng)類(lèi)上添加這個(gè)注解后就可以開(kāi)啟接口的多版本支持。使用Import引入配置ApiAutoConfiguration。

2.將版本號(hào)抽象為ApiItem類(lèi)

ApiItem

@Data
public class ApiItem implements Comparable<ApiItem> {
  private int high = 1;

  private int mid = 0;

  private int low = 0;

  public static final ApiItem API_ITEM_DEFAULT = ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION);

  public ApiItem() {
  }

  @Override
  public int compareTo(ApiItem right) {
    if (this.getHigh() > right.getHigh()) {
      return 1;
    } else if (this.getHigh() < right.getHigh()) {
      return -1;
    }

    if (this.getMid() > right.getMid()) {
      return 1;
    } else if (this.getMid() < right.getMid()) {
      return -1;
    }
    if (this.getLow() > right.getLow()) {
      return 1;
    } else if (this.getLow() < right.getLow()) {
      return -1;
    }
    
    return 0;
  }

}

為了比較版本號(hào)的大小,實(shí)現(xiàn)Comparable接口并重寫(xiě)compareTo(),從高位到低位依次比較。

ApiConverter

public class ApiConverter {

  public static ApiItem convert(String api) {
    ApiItem apiItem = new ApiItem();
    if (StringUtils.isBlank(api)) {
      return apiItem;
    }

    String[] cells = StringUtils.split(api, ".");
    apiItem.setHigh(Integer.parseInt(cells[0]));
    if (cells.length > 1) {
      apiItem.setMid(Integer.parseInt(cells[1]));
    }

    if (cells.length > 2) {
      apiItem.setLow(Integer.parseInt(cells[2]));
    }
    
    return apiItem;
  }

}

ApiConverter提供靜態(tài)方法將字符創(chuàng)轉(zhuǎn)為ApiItem。

常量類(lèi),定義請(qǐng)求頭及默認(rèn)版本號(hào)

public class ApiVersionConstant {
  /**
   * header 指定版本號(hào)請(qǐng)求頭
   */
  public static final String API_VERSION = "x-api-version";

  /**
   * 默認(rèn)版本號(hào)
   */
  public static final String DEFAULT_VERSION = "1.0.0";
}

3.核心ApiCondition 新建ApiCondition類(lèi),實(shí)現(xiàn)RequestCondition,重寫(xiě)combine、getMatchingCondition、compareTo方法。

RequestCondition

public interface RequestCondition<T> {

 /**
 * 方法和類(lèi)上都存在相同的條件時(shí)的處理方法
 */
 T combine(T other);

 /**
 * 判斷是否符合當(dāng)前請(qǐng)求,返回null表示不符合
 */
 @Nullable
 T getMatchingCondition(HttpServletRequest request);

 /**
 *如果存在多個(gè)符合條件的接口,則會(huì)根據(jù)這個(gè)來(lái)排序,然后用集合的第一個(gè)元素來(lái)處理
 */
 int compareTo(T other, HttpServletRequest request);

以上對(duì)RequestCondition簡(jiǎn)要說(shuō)明,后續(xù)詳細(xì)源碼分析各個(gè)方法的作用。

ApiCondition

@Slf4j
public class ApiCondition implements RequestCondition<ApiCondition> {

  public static ApiCondition empty = new ApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION));

  private ApiItem version;

  private boolean NULL;

  public ApiCondition(ApiItem item) {
    this.version = item;
  }

  public ApiCondition(ApiItem item, boolean NULL) {
    this.version = item;
    this.NULL = NULL;
  }

  /**
   * <pre>
   *   Spring先掃描方法再掃描類(lèi),然后調(diào)用{@link #combine}
   *   按照方法上的注解優(yōu)先級(jí)大于類(lèi)上注解的原則處理,但是要注意如果方法上不定義注解的情況。
   *   如果方法或者類(lèi)上不定義注解,我們會(huì)給一個(gè)默認(rèn)的值{@code empty},{@link ApiHandlerMapping}
   * </pre>
   * @param other 方法掃描封裝結(jié)果
   * @return
   */
  @Override
  public ApiCondition combine(ApiCondition other) {
    // 選擇版本最大的接口
    if (other.NULL) {
      return this;
    }
    return other;
  }

  @Override
  public ApiCondition getMatchingCondition(HttpServletRequest request) {
    if (CorsUtils.isPreFlightRequest(request)) {
      return empty;
    }
    String version = request.getHeader(ApiVersionConstant.API_VERSION);
    // 獲取所有小于等于版本的接口;如果前端不指定版本號(hào),則默認(rèn)請(qǐng)求1.0.0版本的接口
    if (StringUtils.isBlank(version)) {
      log.warn("未指定版本,使用默認(rèn)1.0.0版本。");
      version = ApiVersionConstant.DEFAULT_VERSION;
    }
    ApiItem item = ApiConverter.convert(version);
    if (item.compareTo(ApiItem.API_ITEM_DEFAULT) < 0) {
      throw new IllegalArgumentException(String.format("API版本[%s]錯(cuò)誤,最低版本[%s]", version, ApiVersionConstant.DEFAULT_VERSION));
    }
    if (item.compareTo(this.version) >= 0) {
      return this;
    }
    return null;
  }

  @Override
  public int compareTo(ApiCondition other, HttpServletRequest request) {
    // 獲取到多個(gè)符合條件的接口后,會(huì)按照這個(gè)排序,然后get(0)獲取最大版本對(duì)應(yīng)的接口.自定義條件會(huì)最后比較
    int compare = other.version.compareTo(this.version);
    if (compare == 0) {
      log.warn("RequestMappingInfo相同,請(qǐng)檢查!version:{}", other.version);
    }
    return compare;
  }

}

3.配置類(lèi)注入容器

ApiHandlerMapping

public class ApiHandlerMapping extends RequestMappingHandlerMapping {
  @Override
  protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
    return buildFrom(AnnotationUtils.findAnnotation(handlerType, ApiVersion.class));
  }

  @Override
  protected RequestCondition<?> getCustomMethodCondition(Method method) {
    return buildFrom(AnnotationUtils.findAnnotation(method, ApiVersion.class));
  }

  private ApiCondition buildFrom(ApiVersion platform) {
    return platform == null ? getDefaultCondition() :
        new ApiCondition(ApiConverter.convert(platform.value()));
  }

  private ApiCondition getDefaultCondition(){
    return new ApiCondition(ApiConverter.convert(ApiVersionConstant.DEFAULT_VERSION),true);
  }
}

ApiAutoConfiguration
public class ApiAutoConfiguration implements WebMvcRegistrations {

  @Override
  public RequestMappingHandlerMapping getRequestMappingHandlerMapping() {
    return new ApiHandlerMapping();
  }

}

ApiAutoConfiguration沒(méi)有使用Configuration自動(dòng)注入,而是使用Import帶入,目的是可以在程序中選擇性啟用或者不啟用版本控制。

總結(jié)

到此這篇關(guān)于SpringBoot實(shí)現(xiàn)API接口多版本支持的示例代碼的文章就介紹到這了,更多相關(guān)SpringBoot API多版本支持內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 基于java中cookie和session的比較

    基于java中cookie和session的比較

    下面小編就為大家?guī)?lái)一篇基于java中cookie和session的比較。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • Java方法重載Overload原理及使用解析

    Java方法重載Overload原理及使用解析

    這篇文章主要介紹了Java方法重載Overload原理及使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • zookeeper服務(wù)優(yōu)化的一些建議

    zookeeper服務(wù)優(yōu)化的一些建議

    今天小編就為大家分享一篇關(guān)于zookeeper服務(wù)優(yōu)化的一些建議,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Java詳解使用線程池處理任務(wù)方法

    Java詳解使用線程池處理任務(wù)方法

    java中經(jīng)常需要用到多線程來(lái)處理,我們非常不建議單純使用繼承Thread或者實(shí)現(xiàn)Runnable接口的方式來(lái)創(chuàng)建線程,那樣勢(shì)必有創(chuàng)建及銷(xiāo)毀線程耗費(fèi)資源、線程上下文切換問(wèn)題。同時(shí)創(chuàng)建過(guò)多的線程也可能引發(fā)資源耗盡的風(fēng)險(xiǎn),這個(gè)時(shí)候引入線程池比較合理,方便線程任務(wù)的管理
    2022-05-05
  • JavaWeb如何實(shí)現(xiàn)禁用瀏覽器緩存

    JavaWeb如何實(shí)現(xiàn)禁用瀏覽器緩存

    這篇文章主要介紹了JavaWeb如何實(shí)現(xiàn)禁用瀏覽器緩存,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-02-02
  • 詳解DES加密算法的原理與Java實(shí)現(xiàn)

    詳解DES加密算法的原理與Java實(shí)現(xiàn)

    DES 加密,是對(duì)稱加密。對(duì)稱加密,顧名思義,加密和解密的運(yùn)算全都是使用的同樣的秘鑰。這篇文章主要為大家講講DES加密算法的原理與Java實(shí)現(xiàn),需要的可以參考一下
    2022-10-10
  • Spring Boot2.x集成JPA快速開(kāi)發(fā)的示例代碼

    Spring Boot2.x集成JPA快速開(kāi)發(fā)的示例代碼

    這篇文章主要介紹了Spring Boot2.x集成JPA快速開(kāi)發(fā),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • springboot集成shiro自定義登陸過(guò)濾器方法

    springboot集成shiro自定義登陸過(guò)濾器方法

    這篇文章主要介紹了springboot集成shiro自定義登陸過(guò)濾器方法,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Springcloud服務(wù)注冊(cè)consul客戶端過(guò)程解析

    Springcloud服務(wù)注冊(cè)consul客戶端過(guò)程解析

    這篇文章主要介紹了Springcloud服務(wù)注冊(cè)consul客戶端過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • CyclicBarrier線程同步共享變量底層原理示例解析

    CyclicBarrier線程同步共享變量底層原理示例解析

    這篇文章主要為大家介紹了CyclicBarrier線程同步共享變量底層原理示例解析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07

最新評(píng)論