跳到主要內容

多應用下 Swagger 的使用,這可能是最好的方式!

問題


微服務化的時代,我們整個項目工程下面都會有很多的子系統,對於每個應用都有暴露 Api 接口文檔需要,這個時候我們就會想到 Swagger 這個優秀 jar 包。但是我們會遇到這樣的問題,假如說我們有5個應用,難道說我們每個模塊下面都要去引入這個 jar 包嗎?我作為一個比較懶的程序感覺這樣好麻煩,於是乎我思考了一種我認為比較好的方式,如果大家覺得有什麼不太好的地方希望指正,謝謝!


基礎


開始之前大家首先要了解一些基礎,主要有以下幾個方面:



  1. 單應用下 Swagger 的集成與使用

  2. 條件裝配 @Conditional 介紹

  3. 配置文件參數獲取 @ConfigurationProperties


單體應用下 Swagger 集成與使用

關於這部分從3方面講起分別是:什麼是、為什麼、如何用


什麼是 Swagger ?

Swagger 是一個規範且完整的框架,用於生成、描述、調用和可視化 RESTful 風格的 Web 服務。


為什麼使用 Swagger ?

主要的優點:



  1. 支持 API 自動生成同步的在線文檔:使用 Swagger 后可以直接通過代碼生成文檔,不再需要自己手動編寫接口文檔了,對程序員來說非常方便,可以節約寫文檔的時間去學習新技術。

  2. 提供 Web 頁面在線測試 API:光有文檔還不夠,Swagger 生成的文檔還支持在線測試。參數和格式都定好了,直接在界面上輸入參數對應的值即可在線測試接口。


缺點的話就是但凡引入一個 jar 需要去了解下原理和使用,對於這個缺點我感覺相比於優點就是大巫見小巫,我簡單看了一下源碼,其實不算太難。


如何使用 Swagger

關於 Swagger 的使用其實也就是3板斧,大家一定很熟悉的;


第一板斧就是引入 jar 包,這裏我使用的是2.9.2版本


        <!-- swagger 相關 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger2.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger2.version}</version>
        </dependency>

第二板斧就是SpringBoot自動掃描配置類


/**
 * SwaggerConfig
 *
 * @author wangtongzhou
 * @since 2020-06-09 09:41
 */

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                //生產環境的時候關閉 Swagger 比較安全
                .apiInfo(apiInfo())
                .select()
                //Api掃描目錄
                .apis(RequestHandlerSelectors.basePackage("com.springboot2.learning"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("learn")
                .description("learn")
                .version("1.0")
                .build();
    }
}

第三板斧使用 Swagger 註解


/**
 * 用戶相關接口
 *
 * @author wangtongzhou
 * @since 2020-06-12 07:35
 */

@RestController
@RequestMapping("/user")
@Api(value = "用戶相關接口")
public class UserController {

    @PostMapping("/")
    @ApiOperation("添加用戶的接口")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "userName", value = "用戶名", defaultValue =
                    "wtz")
,
            @ApiImplicitParam(name = "age", value = "年齡", defaultValue = "20")
    })
    public User addUser(String userName, Integer age) {
        User user = new User();
        user.setAge(age);
        user.setUserName(userName);
        return user;
    }

    @GetMapping("/{userId}")
    @ApiOperation("根據用戶id查詢用戶信息")
    @ApiImplicitParam(name = "userId", value = "用戶id", defaultValue = "20")
    public User queryUserByUserId(@PathVariable Long userId) {
        User user = new User();
        user.setUserId(userId);
        return user;
    }
}
/**
 * 用戶實體
 *
 * @author wangtongzhou
 * @since 2020-06-12 07:45
 */

@ApiModel
public class User {

    @ApiModelProperty(value = "用戶名稱")
    private String userName;

    @ApiModelProperty(value = "年齡")
    private Integer age;

    @ApiModelProperty(value = "用戶id")
    private Long userId;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }
}

效果如下:

實體註解

接口描述

接口參數

執行接口

返回地址
條件裝配 @Conditional 介紹

@Conditional 是Spring4.0提供的註解,位於 org.springframework.context.annotation 包內,它可以根據代碼中設置的條件裝載不同的bean。比如說當一個接口有兩個實現類時,我們要把這個接口交給Spring管理時通常會只選擇實現其中一個實現類,這個時候我們總不能使用if-else吧,所以這個@Conditional的註解就出現了。


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPEElementType.METHOD})
public @interface Conditional {

    Class<? extends Condition>[] value();

}

使用方法

這裏介紹一個MySQL和Oracle選擇方式,開始之前首先在properties文件中增加sql.name=mysql的配置,接下來步驟如下



  1. 實現Conditional接口, 實現matches方法


/**
 * mysql條件裝配
 *
 * @author wangtongzhou
 * @since 2020-06-13 08:01
 */

public class MysqlConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String sqlName = context.getEnvironment().getProperty("sql.name");
        if ("mysql".equals(sqlName)){
            return true;
        }
        return false;
    }
}
/**
 * oracle條件裝配
 *
 * @author wangtongzhou
 * @since 2020-06-13 08:02
 */

public class OracleConditional implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String sqlName=context.getEnvironment().getProperty("sql.name");
        if ("oracle".equals(sqlName)){
            return true;
        }
        return false;
    }
}


  1. 在需要判斷條件的bean上,加上@Conditional(***.class)即可在滿足條件的時候加載對應的類


/**
 * conditional
 *
 * @author wangtongzhou
 * @since 2020-06-13 08:01
 */

@Configuration
public class ConditionalConfig {

    @Bean
    @Conditional(MysqlConditional.class)
    public Mysql mysql() {
        return new Mysql();
    }

    @Bean
    @Conditional(OracleConditional.class)
    public Oracle oracle() {
        return new Oracle();
    }
}


  1. 調用測試


@RunWith(SpringRunner.class)
@SpringBootTest
public class ConditionalTests {

    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void test_conditional() {
        Mysql mysql = (Mysql) applicationContext.getBean("mysql");
        Assert.assertNotNull(mysql);
        Assert.assertTrue("mysql".equals(mysql.getSqlName()));
    }
}

其他擴展註解


  1. @@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
    當存在Docket和ApiInfoBuilder類的時候才加載Bean;

  2. @ConditionalOnMissingClass不存在某個類的時候才會實例化Bean;

  3. @ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true)當存在swagger為前綴的屬性,才會實例化Bean;

  4. @ConditionalOnMissingBean當不存在某個Bean的時候才會實例化;


這裏就介紹這幾個常用,org.springframework.boot.autoconfigure.condition這個下面包含全部的關於@Conditional相關的所有註解

@Conditional擴展

註解介紹
配置文件參數獲取 @ConfigurationProperties

@ConfigurationProperties是SpringBoot加入的註解,主要用於配置文件中的指定鍵值對映射到一個Java實體類上。關於這個的使用就在下面的方式引出。


比較好的方式


關於開篇中引入的問題,解題流程主要是以下3步:



  1. 抽象一個公共 Swagger jar;

  2. 如何定製化 Swagger jar;

  3. 使用定製化完成以後的 Swagger jar;


抽象一個公共 Swagger jar

主要是就是將 Swagger jar 和一些其他需要的 jar 進行引入,使其成為一個公共的模塊,軟件工程中把這個叫做單一原則;



Maven包的引入

 


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
    </dependencies>

如何定製化 Swagger jar;

定製化就是將 Swagger 相關的屬性進行配置化的處理,這裏也可以分為兩步;



  1. 將公共的屬性抽象成配置化的類,這裏就是關於@ConfigurationProperties的使用,將配置文件中的 swagger 開頭的屬性映射到配置類的屬性當中;


/**
 * Swagger基本屬性
 *
 * @author wangtongzhou
 * @since 2020-05-24 16:58
 */

@ConfigurationProperties("swagger")
public class SwaggerProperties {

    /**
     * 子系統
     */

    private String title;

    /**
     * 描述
     */

    private String description;

    /**
     * 版本號
     */

    private String version;

    /**
     * api包路徑
     */

    private String basePackage;

    public String getTitle() {
        return title;
    }

    public SwaggerProperties setTitle(String title) {
        this.title = title;
        return this;
    }

    public String getDescription() {
        return description;
    }

    public SwaggerProperties setDescription(String description) {
        this.description = description;
        return this;
    }

    public String getVersion() {
        return version;
    }

    public SwaggerProperties setVersion(String version) {
        this.version = version;
        return this;
    }

    public String getBasePackage() {
        return basePackage;
    }

    public SwaggerProperties setBasePackage(String basePackage) {
        this.basePackage = basePackage;
        return this;
    }
}


  1. 公共屬性賦值配置到 Swagger 的配置類中,該配置類中進行一些類條件的判斷和插件Bean是否已經注入過,然後就是將配置類中的屬性,賦值到 Swagger 的初始化工程中;


/**
 * Swagger自動配置類
 *
 * @author wangtongzhou
 * @since 2020-05-24 16:35
 */

@Configuration
@EnableSwagger2
@ConditionalOnClass({Docket.class, ApiInfoBuilder.class})
@ConditionalOnProperty(prefix = "swagger", value = "enable", matchIfMissing = true)
@EnableConfigurationProperties(SwaggerProperties.class)
public class SwaggerConfig {

    @Bean
    @ConditionalOnMissingBean
    public SwaggerProperties swaggerProperties() {
        return new SwaggerProperties();
    }

    @Bean
    public Docket createRestApi() {
        SwaggerProperties properties = swaggerProperties();
        return new Docket(DocumentationType.SWAGGER_2)
                //生產環境的時候關閉 Swagger 比較安全
                .apiInfo(apiInfo(properties))
                .select()
                //Api掃描目錄
                .apis(RequestHandlerSelectors.basePackage(properties.getBasePackage()))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo(SwaggerProperties properties) {
        return new ApiInfoBuilder()
                .title(properties.getTitle())
                .description(properties.getDescription())
                .version(properties.getVersion())
                .build();
    }

}

完成以上兩步,就完成了 Swagger 模塊的定製化開發,接下來還要做一件事情,作為一個公共的模塊,我們要讓他自己進行自動化裝配,解放我們的雙手,我們在 resources 目錄下增加一個 spring.factories 配置文件,內容如下:


org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.springcloud.study.swagger.config.SwaggerConfig

到此我們完成所有的開發;


使用定製化完成以後的 Swagger jar;

關於使用也分為兩步,



  1. 引入 jar;


        <dependency>
            <groupId>com.springcloud.study</groupId>
            <artifactId>common-swagger</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>


  1. 定製化的屬性配置;


Swagger 配置項
swagger:
  title: 用戶模塊
  description: 用戶子系統
  version: 1.0.0
  base-packagecom.springcloud.study.user.controller

完成這兩步就可以開啟正常的使用了;


後續的規劃



  1. 註解整理
    後續會將 Spring 註解進行一個統一的整理,包含一些使用說明或者原理等等,希望到時候能幫助到大家吧,目前計劃兩周一個吧;

  2. 開源項目
    Spring Cloud 的學習過於碎片化,希望通過自己搞一個開源項目,提升對各個組件的掌握能力,同時也能產出一套通用化權限管理系統,具備很高的靈活性、擴展性和高可用性,並且簡單易用,這塊是和未來做企業数字化轉型相關的事是重合的,慢慢的會做一些企業級通用化的的功能開發;前端部分的話希望是採用Vue,但是這塊有一個學習成本,還沒有進行研究,目前還沒排上日程。整體的里程碑是希望在6.22離職之前完成整套後端的開發,7月中旬完成第一次Commit。


點點關注


這邊文章限於篇幅,過多的關注於使用了,後續會把上面幾個註解的原理分析講講,歡迎大家點點關注,點點贊,感謝!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】



※為什麼 USB CONNECTOR 是電子產業重要的元件?



網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!



※台北網頁設計公司全省服務真心推薦



※想知道最厲害的網頁設計公司"嚨底家"!



※推薦評價好的iphone維修中心




Orignal From: 多應用下 Swagger 的使用,這可能是最好的方式!

留言

這個網誌中的熱門文章

Python 併發總結,多線程,多進程,異步IO

1 測量函數運行時間 import time def profile(func): def wrapper(*args, ** kwargs): import time start = time.time() func( *args, ** kwargs) end = time.time() print ' COST: {} ' .format(end - start) return wrapper @profile def fib(n): if n<= 2 : return 1 return fib(n-1) + fib(n-2 ) fib( 35 )   2 啟動多個線程,並等待完成   2.1 使用threading.enumerate() import threading for i in range(2 ): t = threading.Thread(target=fib, args=(35 ,)) t.start() main_thread = threading.currentThread() for t in threading.enumerate(): if t is main_thread: continue t.join()   2.2 先保存啟動的線程 threads = [] for i in range(5 ): t = Thread(target=foo, args= (i,)) threads.append(t) t.start() for t in threads: t.join()   3 使用信號量,限制同時能有幾個線程訪問臨界區 from threading import Semaphore import time sema = Semaphor...

韋伯連續劇終於更新 期待第一季順利完結

  地球天文學界的跳票大王詹姆斯·韋伯空間望遠鏡 (James Webb Space Telescope,縮寫為 JWST)自 1996 年以來斷斷續續不按劇本演出的連續劇終於讓焦慮的觀眾們又等到了一次更新:五層遮陽罩測試順利完成。 裝配完成的韋伯望遠鏡與好夥伴遮陽罩同框啦。Credit: NASA   嚴格的測試是任何空間任務順利成功的重中之重。遮陽罩,這個韋伯望遠鏡異常重要的親密夥伴,要是無法正常運轉的話,韋伯的這一季天文界連續劇說不準就要一直拖更了。   詹姆斯·韋伯空間望遠鏡是歷史上造出的最先進的空間望遠鏡。它不僅是一架紅外望遠鏡,還具有特別高的靈敏度。但想要達到辣么高的靈敏度來研究系外行星和遙遠的宇宙童年,韋伯童鞋必須非常"冷靜",體溫升高的話,靈敏度會大大折損。這個時候,遮陽罩就要大顯身手啦。   遮陽罩在韋伯的設計中至關重要。韋伯望遠鏡會被發射到拉格朗日 L2 點,運行軌道很高,遠離太陽、地球與月球。太陽是韋伯的主要熱量干擾的來源,其次是地球與月球。遮陽罩會有效阻斷來自這三大熱源的能量並保護韋伯維持在工作溫度正常運轉。這個工作溫度指的是零下 220 攝氏度(-370 華氏度;50 開爾文)。 上圖中我們可以看出,韋伯望遠鏡的配置大致可分為兩部分:紅色較熱的一面溫度為 85 攝氏度,藍色較冷的一面溫度達到零下 233 攝氏度。紅色的這部分中,儀器包括太陽能板、通信設備、計算機、以及轉向裝置。藍色部分的主要裝置包括鏡面、探測器、濾光片等。Credit: STSci.   遮陽罩的那一部分和望遠鏡的鏡面這部分可以產生非常極端的溫差。遮陽的這面溫度可以達到 110 攝氏度,足以煮熟雞蛋,而背陰處的部分溫度極低,足以凍結氧氣。   工程師們剛剛完成了五層遮陽罩的測試,按照韋伯在 L2 時的運行狀態安裝了遮陽罩。L2 距離地球約 160 萬公里。NASA 表示這些測試使用了航天器的自帶系統來展開遮陽罩,測試目前都已成功完成。韋伯望遠鏡遮陽罩負責人 James Cooper 介紹說這是遮陽罩"第一次在望遠鏡系統的电子設備的控制下展開。儘管這個任務非常艱巨,難度高,但測試順利完成,遮陽罩展開時的狀態非常驚艷"。   遮陽罩由五層 Kapton 製成。Kapton 是一種聚酰亞胺薄膜材料, 耐高溫絕...

LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳

» » LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳 消費明細好清楚,不怕算錯錢啦! by in , 讀取中... 之前介紹過的「LINE 發票管家」,除了能對統一發票、管理消費紀錄,現在還能分享消費明細給其他朋友囉!趕快來看看該如何分享吧!讓大家在聚餐過後,不用再截圖、拍照把消費明細記錄下來分享到 LINE 好友群組,只要用 LINE發票管家就可以隨時一鍵分享消費明細,包括店家、消費時間、消費項目、金額通通都有,聚餐 AA 也更好算帳。 LINE 發票管家「一鍵分享發票」新功能,聚餐AA更好算帳 朋友聚餐完,經常會先由其中一位買單再事後收帳,不過難免擔心忘記拍照紀錄下消費明細,導致算帳變得很麻煩。但是,現在只要用 LINE發票管家,不僅能將發票存入載具後直接匯入發票,進而更方便管理每月的消費情況,現在還能用它來分享消費明細到 LINE 聊天室,不需要截圖,整個流程超級簡單! ▲圖片來源: 當使用 LINE發票管家並且綁定載具後,只要日常消費有將發票存入載具就通通會自動匯入 LINE發票管家。如果想暸解自己近期的消費情況,也能在 LINE發票管家點選「發票明細」查詢所有消費紀錄。 *小提醒:存入手機條碼的發票,待財政部 1-2 日作業時間將發票資料匯入後, 打開單筆消費後分享到指定對象或群組。 像是朋友聚餐或其它購物消費,就能在 LINE發票管家查看每一筆消費的所有消費明細,每一項餐點的項目、價格通通一目了然。如果想將該筆消費內容分享給好友收帳,只要點選該筆消費明細後,接著點選畫面右上角的「分享」按鈕。 網頁設計 最專業,超強功能平台可客製,窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機,請問 台中電動車 哪裡在賣比較便宜可以到台中景泰電動車門市去看看總店:臺中市潭子區潭秀里雅潭路一段102-1號。 電動車補助 推薦評價好的 iphone維修 中心擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢住家的頂樓裝 太陽光電 聽說可發揮隔熱功效一線推薦東陽能源擁有核心技術、...