今天我們來說說什麼是Swagger?
就是把相關的信息存儲在它定義的描述文件裡面(yml或json格式),再通過維護這個描述文件可以去更新接口文檔,以及生成各端代碼。而Springfox-swagger,則可以通過掃描代碼去生成這個描述文件。
好處:
1、是一款讓你更好的書寫API文檔的規範且完整框架。
2、提供描述、生產、消費和可視化RESTful Web Service。
3、是由龐大工具集合支撐的形式化規範。這個集合涵蓋了從終端用戶接口、底層代碼庫到商業API管理的方方面面。
4,這個框架可以自動為你的業務代碼生成restfut風格的api,而且還提供相應的測試界面,自動顯示json格式的響應。大大方便了後臺開發人員與前端的溝通與聯調成本
缺點:
如果使用的接口參數和返回值相對比較簡單,即如果都是處理成單表操作的接口,那麼使用swagger是明智選擇,但是如果接口參數本身很複雜,接口的返回值同樣非常複雜,那麼swagger的作用就會縮小.因此複雜業務的接口文檔或者wiki編寫是必需的,簡單的接口或者單表操作使用swagger。
swagger進行API管理
目前 springfox 是一個很好的選擇,它內部會自動解析Spring容器中Controller暴露出的接口,並且也提供了一個界面用於展示或調用這些API。下圖就是簡單的一個使用springfox的API展示界面。
springfox的前身是swagger-springmvc,用於springmvc與swagger的整合。
如若在springboot項目中使用springfox,需要3個步驟:
1、maven添加springfox依賴
2、啟動類加上@EnableSwagger2註解
3、構造Docket bean用於展示API
配置完之後進入 http://{path}:{port}/swagger-ui.html 即可查看controller中的接口信息,並按照Docket中配置的規則進行展示。
springfox實現原理
在分析springfox實現原理之前,首先看下springfox對文檔Documentation的定義:
文檔Documentation定義得很清晰,主要由groupName(分組名)、basePath(contextPath)、apiListings(API列表集)、resourceListing(資源列表集)等屬性組成。
其中API列表被封裝成ApiListing。ApiListing中又持有ApiDesciption集合引用,每個ApiDesciption都持有一個API集合的引用,Operation也就是具體的接口操作,內部包含了該接口對應的http方法、produces、consumes、協議、參數集、響應消息集等諸多元素。
springfox通過spring-plugin的方式將Plugin註冊到Spring上下文中,然後使用這些plugin進行API的掃描工作,這裡的掃描工作其實也就是構造Documentation的工作,把掃描出的結果封裝成Documentation並放入到DocumentationCache內存緩存中,之後swagger-ui界面展示的API信息通過Swagger2Controller暴露,Swagger2Controller內部直接從DocumentationCache中尋找Documentation。
下圖就是部分Plugin具體構造對應的文檔信息:
代碼細節方面的分析:
很明顯,入口處在@EnableSwagger2註解上,該註解會import一個配置類Swagger2DocumentationConfiguration。
Swagger2DocumentationConfiguration做的事情:
1、構造Bean。比如HandlerMapping,HandlerMapping是springmvc中用於處理請求與handler(controller中的方法)之間映射關係的接口,springboot中默認使用的HandlerMapping是RequestMappingHandlerMapping,Swagger2DocumentationConfiguration配置類裡構造的是PropertySourcedRequestMappingHandlerMapping,該類繼承RequestMappingHandlerMapping。
2、import其它配置類,比如SpringfoxWebMvcConfiguration、SwaggerCommonConfiguration
3、掃描指定包下的類,並註冊到Spring上下文中
SpringfoxWebMvcConfiguration配置類做的事情跟Swagger2DocumentationConfiguration類似,不過多了一步構造PluginRegistry過程。該過程使用@EnablePluginRegistries註解實現:
@EnablePluginRegistries註解是spring-plugin模塊提供的一個基於Plugin類型註冊PluginRegistry實例到Spring上下文的註解。
@EnablePluginRegistries註解內部使用PluginRegistriesBeanDefinitionRegistrar註冊器去獲取註解的value屬性(類型為Plugin接口的Class數組);然後遍歷這個Plugin數組,針對每個Plugin在Spring上下文中註冊PluginRegistryFactoryBean,並設置相應的name和屬性。
如果處理的Plugin有@Qualifier註解,那麼這個要註冊的PluginRegistryFactoryBean的name就是@Qualifier註解的value,否則name就是插件名首字母小寫+Registry的格式(比如DocumentationPlugin對應構造的bean的name就是documentationPluginRegistry)。
PluginRegistriesBeanDefinitionRegistrar註冊器處理過程:
PluginRegistryFactoryBean是一個FactoryBean,其內部真正構造的bean的類型是OrderAwarePluginRegistry。OrderAwarePluginRegistry實例化過程中會調用create靜態方法,傳入的plugin集合使用aop代理生成一個ArrayList,這個list中的元素就是Spring上下文中所有的類型為之前遍歷的Plugin的bean。
PluginRegistryFactoryBean的getObject方法:
這裡的targetSource是在PluginRegistryFactoryBean的父類AbstractTypeAwareSupport(實現了InitializingBean接口)中的afterPropertiesSet方法中初始化的(type屬性在PluginRegistriesBeanDefinitionRegistrar註冊器中已經設置為遍歷的Plugin):
BeansOfTypeTargetSource的getTarget方法:
舉個例子:比如SpringfoxWebMvcConfiguration中的@EnablePluginRegistries註解裡的DocumentationPlugin這個Plugin,在處理過程中會找出Spring上下文中所有的Docket(Docket實現了DocumentationPlugin接口),並把該集合設置成name為documentationPluginRegistry、類型為OrderAwarePluginRegistry的bean,註冊到Spring上下文中。
DocumentationPluginsManager類會在之前提到過的配置類中被掃描出來,它內部的各個pluginRegistry屬性都是@EnablePluginRegistries註解內部構造的各種pluginRegistry實例:
DocumentationPluginsBootstrapper啟動類也會在之前提供的配置類中被掃描出來。它實現了SmartLifecycle接口,在start方法中,會獲取之前初始化的所有documentationPlugins(也就是Spring上下文中的所有Docket)。遍歷這些Docket並進行scan掃描(使用RequestMappingHandlerMapping的getHandlerMethods方法獲取url與方法的所有映射關係,然後進行一系列API解析操作),掃描出來的結果封裝成Documentation並添加到DocumentationCache中:
以上就是API解析、掃描的大致處理過程,整理如下:
下面分析一下HandlerMapping的處理過程。
PropertySourcedRequestMappingHandlerMapping在Swagger2DocumentationConfiguration配置類中被構造:
PropertySourcedRequestMappingHandlerMapping初始化過程中會設置優先級為Ordered.HIGHEST_PRECEDENCE + 1000,同時還會根據Swagger2Controller得到RequestMappingInfo映射信息,並設置到handlerMethods屬性中。
PropertySourcedRequestMappingHandlerMapping複寫了lookupHandlerMethod方法,首先會去handlerMethods屬性中查詢是否存在對應的映射關係,沒找到的話使用下一個HandlerMapping進行處理:
Swagger2Controller中只有一個mapping方法,默認的path值為/v2/api-docs,可以通過配置 springfox.documentation.swagger.v2.path 進行修改。所以默認情況下 /v2/api-docs?group=person-api、/v2/api-docs?group=user-api 這些地址都會被Swagger2Controller所處理。
Swagger2Controller內部獲取文檔信息會去DocumentationCache中查找:
引入springfox帶來的影響
影響主要有2點:
應用啟動速度變慢,因為額外加載了springfox中的信息,同時內存中也緩存了這些API信息
多了一個HandlerMapping,並且優先級高。以下是springboot應用DispatcherServlet的HandlerMapping集合。其中springfox構造的PropertySourcedRequestMappingHandlerMapping優先級最高。優先級最高說明第一次查詢映射關係都是走PropertySourcedRequestMappingHandlerMapping,而程序中大部分請求都是在RequestMappingHandlerMapping中處理的
優先級問題可以使用BeanPostProcessor處理,修改優先級:
SpringBoot項目配置 swagger
<code>@EnableSwagger2@Configurationpublic class Swagger2Config { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() //為當前包路徑 .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build(); } //構建 api文檔的詳細信息函數 private ApiInfo apiInfo() { return new ApiInfoBuilder() //頁面標題 .title("功能測試") //創建人 .contact(new Contact("Edison", "[email protected]", "[email protected]")) //版本號 .version("1.0") //描述 .description("API 描述") .build(); }/<code>
maven.xml
<code><dependency> <groupid>io.springfox/<groupid> <artifactid>springfox-swagger2/<artifactid> <version>2.6.1/<version>/<dependency><dependency> <groupid>io.springfox/<groupid> <artifactid>springfox-swagger-ui/<artifactid> <version>2.6.1/<version>/<dependency>/<code>
這是ui把所有暴露成api。是不是非常簡單。喜歡記得點贊轉發。
閱讀更多 程序員小暢 的文章