swagger是一個API框架,號稱世界上最流行的API工具。它提供了API管理的全套解決方案,好比API在線編輯器,API UI展現界面,代碼生成器等諸多功能。html
若是想引入swagger進行API管理。目前 springfox 是一個很好的選擇,它內部會自動解析Spring容器中Controller暴露出的接口,而且也提供了一個界面用於展現或調用這些API。下圖就是簡單的一個使用springfox的API展現界面。spring
springfox的前身是swagger-springmvc,用於springmvc與swagger的整合。api
如若在springboot項目中使用springfox,須要3個步驟:數組
一、maven添加springfox依賴緩存
二、啓動類加上@EnableSwagger2註解springboot
三、構造Docket bean用於展現APImvc
配置完以後進入 http://{path}:{port}/swagger-ui.html 便可查看controller中的接口信息,並按照Docket中配置的規則進行展現。app
在分析springfox實現原理以前,首先看下springfox對文檔Documentation的定義:框架
文檔Documentation定義得很清晰,主要由groupName(分組名)、basePath(contextPath)、apiListings(API列表集)、resourceListing(資源列表集)等屬性組成。maven
其中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作的事情:
一、構造Bean。好比HandlerMapping,HandlerMapping是springmvc中用於處理請求與handler(controller中的方法)之間映射關係的接口,springboot中默認使用的HandlerMapping是RequestMappingHandlerMapping,Swagger2DocumentationConfiguration配置類裏構造的是PropertySourcedRequestMappingHandlerMapping,該類繼承RequestMappingHandlerMapping。
二、import其它配置類,好比SpringfoxWebMvcConfiguration、SwaggerCommonConfiguration
三、掃描指定包下的類,並註冊到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中查找:
影響主要有2點:
應用啓動速度變慢,由於額外加載了springfox中的信息,同時內存中也緩存了這些API信息
多了一個HandlerMapping,而且優先級高。如下是springboot應用DispatcherServlet的HandlerMapping集合。其中springfox構造的PropertySourcedRequestMappingHandlerMapping優先級最高。優先級最高說明第一次查詢映射關係都是走PropertySourcedRequestMappingHandlerMapping,而程序中大部分請求都是在RequestMappingHandlerMapping中處理的
優先級問題可使用BeanPostProcessor處理,修改優先級:
本文做者:中間件小哥
閱讀原文本文爲雲棲社區原創內容,未經容許不得轉載。