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处理,修改优先级:
本文做者:中间件小哥
阅读原文本文为云栖社区原创内容,未经容许不得转载。