Springfox-Swagger源码分析
微信公众号文章列表:关注公众号(coding_song)阅读更清晰,附件为微信二维码
原文链接:https://mp.weixin.qq.com/s/c3uU_t2pejZe4ybQ-wVcOg
Swagger依赖
-
<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>
EnableSwagger2配置
通过注解EnableSwagger2导入spring需要扫描的包路径
-
@Configuration
-
@EnableSwagger2
-
publicclassSwaggerConfiguration{
-
...
-
}
EnableSwagger2注解
导入Swagger2DocumentationConfiguration注解的配置
-
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
-
@Target(value ={ java.lang.annotation.ElementType.TYPE })
-
@Documented
-
@Import({Swagger2DocumentationConfiguration.class})
-
public@interfaceEnableSwagger2{
-
}
Swagger2DocumentationConfiguration
导入配置类SpringfoxWebMvcConfiguration和SwaggerCommonConfiguration,Spring将扫描这两个类中配置的包路径下的类
-
@Configuration
-
@Import({SpringfoxWebMvcConfiguration.class,SwaggerCommonConfiguration.class})
-
@ComponentScan(basePackages ={
-
"springfox.documentation.swagger2.readers.parameter",
-
"springfox.documentation.swagger2.web",
-
"springfox.documentation.swagger2.mappers"
-
})
-
publicclassSwagger2DocumentationConfiguration{
-
@Bean
-
publicJacksonModuleRegistrar swagger2Module(){
-
returnnewSwagger2JacksonModule();
-
}
-
}
SpringfoxWebMvcConfiguration
添加扫描的包,并按照顺序依次注册相应的插件,这些插件统一维护在DocumentationPluginsManager中
-
@Configuration
-
@Import({ModelsConfiguration.class})
-
@ComponentScan(basePackages ={
-
"springfox.documentation.spring.web.scanners",
-
"springfox.documentation.spring.web.readers.operation",
-
"springfox.documentation.spring.web.readers.parameter",
-
"springfox.documentation.spring.web.plugins",
-
"springfox.documentation.spring.web.paths"
-
})
-
@EnablePluginRegistries({DocumentationPlugin.class,
-
ApiListingBuilderPlugin.class,
-
OperationBuilderPlugin.class,
-
ParameterBuilderPlugin.class,
-
ExpandedParameterBuilderPlugin.class,
-
ResourceGroupingStrategy.class,
-
OperationModelsProviderPlugin.class,
-
DefaultsProviderPlugin.class,
-
PathDecorator.class
-
})
-
publicclassSpringfoxWebMvcConfiguration{
-
//省略实现的代码
-
}
-
//DocumentationPluginsManager管理维护上述插件
-
@Component
-
publicclassDocumentationPluginsManager{
-
@Autowired
-
@Qualifier("documentationPluginRegistry")
-
privatePluginRegistry<DocumentationPlugin,DocumentationType> documentationPlugins;
-
@Autowired
-
@Qualifier("apiListingBuilderPluginRegistry")
-
privatePluginRegistry<ApiListingBuilderPlugin,DocumentationType> apiListingPlugins;
-
@Autowired
-
@Qualifier("parameterBuilderPluginRegistry")
-
privatePluginRegistry<ParameterBuilderPlugin,DocumentationType> parameterPlugins;
-
@Autowired
-
@Qualifier("expandedParameterBuilderPluginRegistry")
-
privatePluginRegistry<ExpandedParameterBuilderPlugin,DocumentationType> parameterExpanderPlugins;
-
@Autowired
-
@Qualifier("operationBuilderPluginRegistry")
-
privatePluginRegistry<OperationBuilderPlugin,DocumentationType> operationBuilderPlugins;
-
@Autowired
-
@Qualifier("resourceGroupingStrategyRegistry")
-
privatePluginRegistry<ResourceGroupingStrategy,DocumentationType> resourceGroupingStrategies;
-
@Autowired
-
@Qualifier("operationModelsProviderPluginRegistry")
-
privatePluginRegistry<OperationModelsProviderPlugin,DocumentationType> operationModelsProviders;
-
@Autowired
-
@Qualifier("defaultsProviderPluginRegistry")
-
privatePluginRegistry<DefaultsProviderPlugin,DocumentationType> defaultsProviders;
-
@Autowired
-
@Qualifier("pathDecoratorRegistry")
-
privatePluginRegistry<PathDecorator,DocumentationContext> pathDecorators;
-
@Autowired(required =false)
-
@Qualifier("apiListingScannerPluginRegistry")
-
privatePluginRegistry<ApiListingScannerPlugin,DocumentationType> apiListingScanners;
-
/** 省略部分代码 */
-
}
SwaggerCommonConfiguration
Spring会扫描配置包路径下的Bean
-
@Configuration
-
@ComponentScan(basePackages ={
-
"springfox.documentation.swagger.schema",
-
"springfox.documentation.swagger.readers",
-
"springfox.documentation.swagger.web"
-
})
-
publicclassSwaggerCommonConfiguration{
-
}
Spring创建并加载Swagger的引导类
DocumentationPluginsBootstrapper引导类作为Springfox的入口,需先由Spring创建并加载到Spring容器中
Spring整合Springfox流程
启动SpringBoot后,在刷新应用程序上下文时,会检索所有可用的、已经创建好单例的Lifecycle beans,以及检索所有的SmartLifecycle beans,并启动这些生命周期Bean
源码分析
SpringApplication源码
-
publicConfigurableApplicationContext run(String... args){
-
/** 省略部分代码 */
-
//刷新应用程序上下文
-
this.refreshContext(context);
-
/** 省略部分代码 */
-
}
AbstractApplicationContext.refresh():Spring在finishBeanFactoryInitialization(beanFactory)方法实例化所有扫描到的单例
-
@Override
-
publicvoid refresh()throwsBeansException,IllegalStateException{
-
synchronized(this.startupShutdownMonitor){
-
/** 省略部分代码 */
-
// Instantiate all remaining (non-lazy-init) singletons. 实例化所有剩余的非懒初始化的单例
-
finishBeanFactoryInitialization(beanFactory);
-
// Last step: publish corresponding event. 发布相应的事件,具体刷新操作在这里面执行
-
finishRefresh();
-
}
-
/** 省略部分代码 */
-
}
-
/**
-
* Finish the initialization of this context's bean factory,
-
* initializing all remaining singleton beans.
-
*/
-
protectedvoid finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory){
-
/** 省略部分代码 */
-
// Instantiate all remaining (non-lazy-init) singletons.
-
beanFactory.preInstantiateSingletons();
-
}
DefaultListableBeanFactory中遍历所有单例来创建bean实例,包含Springfox的引导类DocumentationPluginsBootstrapper、Swagger配置的Docket Bean等。创建好所有的Bean后,执行刷新操作,刷新即启动一些生命周期的Bean,DefaultLifecycleProcessor源码如下
-
@Override
-
publicvoid refresh()throwsBeansException,IllegalStateException{
-
synchronized(this.startupShutdownMonitor){
-
// Last step: publish corresponding event. 发布相应的事件,具体刷新操作在这里面执行
-
finishRefresh();
-
}
-
/** 省略部分代码 */
-
}
-
protectedvoid finishRefresh(){
-
// Initialize lifecycle processor for this context.
-
initLifecycleProcessor();
-
// Propagate refresh to lifecycle processor first. 刷新LifecycleBean
-
getLifecycleProcessor().onRefresh();
-
// Publish the final event.
-
publishEvent(newContextRefreshedEvent(this));
-
// Participate in LiveBeansView MBean, if active.
-
LiveBeansView.registerApplicationContext(this);
-
}
-
@Override
-
publicvoid onRefresh(){
-
startBeans(true);
-
this.running =true;
-
}
DefaultLifecycleProcessor启动生命周期Bean:先获取生命周期Bean,然后再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true
-
privatevoid startBeans(boolean autoStartupOnly){
-
//获取生命周期Bean
-
Map<String,Lifecycle> lifecycleBeans = getLifecycleBeans();
-
//创建Map来维护LifecycleGroup
-
Map<Integer,LifecycleGroup> phases =newHashMap<Integer,LifecycleGroup>();
-
for(Map.Entry<String,?extendsLifecycle> entry : lifecycleBeans.entrySet()){
-
Lifecycle bean = entry.getValue();
-
//再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true
-
if(!autoStartupOnly ||(bean instanceofSmartLifecycle&&((SmartLifecycle) bean).isAutoStartup())){
-
//判断当前Bean是否继承Phased,如果是,则返回该Bean的相对值
-
int phase = getPhase(bean);
-
//LifecycleGroup用于维护一组Lifecycle bean
-
LifecycleGroup group = phases.get(phase);
-
if(group ==null){
-
//创建一组LifecycleGroup
-
group =newLifecycleGroup(phase,this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
-
//保存LifecycleGroup
-
phases.put(phase, group);
-
}
-
//将当前bean存入LifecycleGroup
-
group.add(entry.getKey(), bean);
-
}
-
}
-
if(!phases.isEmpty()){
-
List<Integer> keys =newArrayList<Integer>(phases.keySet());
-
Collections.sort(keys);
-
for(Integer key : keys){
-
//启动bean
-
phases.get(key).start();
-
}
-
}
-
}
-
protectedMap<String,Lifecycle> getLifecycleBeans(){
-
Map<String,Lifecycle> beans =newLinkedHashMap<String,Lifecycle>();
-
//获取所有继承Lifecycle类的、符合条件的子类名称
-
String[] beanNames =this.beanFactory.getBeanNamesForType(Lifecycle.class,false,false);
-
for(String beanName : beanNames){
-
//Bean名称
-
String beanNameToRegister =BeanFactoryUtils.transformedBeanName(beanName);
-
//判断当前Bean名称对应的实例,是否是FactoryBean
-
boolean isFactoryBean =this.beanFactory.isFactoryBean(beanNameToRegister);
-
String beanNameToCheck =(isFactoryBean ?BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);
-
//如果beanFactory有当前bean的实例、且Lifecycle或SmartLifecycle是当前bean的超类,则获取当前beanName的实例,将其存放到Map中返回
-
if((this.beanFactory.containsSingleton(beanNameToRegister)&&
-
(!isFactoryBean ||Lifecycle.class.isAssignableFrom(this.beanFactory.getType(beanNameToCheck))))||
-
SmartLifecycle.class.isAssignableFrom(this.beanFactory.getType(beanNameToCheck))){
-
Lifecycle bean =this.beanFactory.getBean(beanNameToCheck,Lifecycle.class);
-
if(bean !=this){
-
beans.put(beanNameToRegister, bean);
-
}
-
}
-
}
-
return beans;
-
}
-
protectedint getPhase(Lifecycle bean){
-
return(bean instanceofPhased?((Phased) bean).getPhase():0);
-
}
-
//在Bean中自定义的
-
@Override
-
publicint getPhase(){
-
returnInteger.MAX_VALUE;
-
}
至此,Spring整合Springfox就结束了,接下来是Spring刷新上下文是,初始化Springfox的引导类DocumentationPluginsBootstrapper,并进行相应的配置
启动Swagger引导类
引导类DocumentationPluginsBootstrapper是Springfox的入口,Swagger的所有配置初始化都是在里面进行
DocumentationPluginsBootstrapper启动流程图
源码分析
DocumentationPluginsBootstrapper.start()
-
@Override
-
publicvoid start(){
-
if(initialized.compareAndSet(false,true)){
-
log.info("Context refreshed");
-
// 获取所有的Docket实例
-
List<DocumentationPlugin> plugins = pluginOrdering().sortedCopy(documentationPluginsManager.documentationPlugins());
-
log.info("Found {} custom documentation plugin(s)", plugins.size());
-
//遍历每个Docket
-
for(DocumentationPlugin each : plugins){
-
DocumentationType documentationType = each.getDocumentationType();
-
if(each.isEnabled()){
-
//初始化Docket配置,并保存到内存中
-
scanDocumentation(buildContext(each));
-
}else{
-
log.info("Skipping initializing disabled plugin bean {} v{}",
-
documentationType.getName(), documentationType.getVersion());
-
}
-
}
-
}
-
}
首先先组装Docket基本信息,获取项目所有RequestMapping的接口将其放到DocumentationContextBuilder中
-
privateDocumentationContext buildContext(DocumentationPlugin each){
-
return each.configure(defaultContextBuilder(each));
-
}
-
privateDocumentationContextBuilder defaultContextBuilder(DocumentationPlugin each){
-
DocumentationType documentationType = each.getDocumentationType();
-
//获取所有RequestMapping的接口
-
List<RequestHandler> requestHandlers = from(handlerProviders)
-
.transformAndConcat(handlers())
-
.toList();
-
//将所有请求接口放到DocumentationContextBuilder中
-
return documentationPluginsManager
-
.createContextBuilder(documentationType, defaultConfiguration)
-
.requestHandlers(requestHandlers);
-
}
装好Docket后,然后过滤出当前Docket配置的包路径下的接口,Docket有一个插件管理器DocumentationPluginsManager用于维护Docket的多个插件
-
privatevoid scanDocumentation(DocumentationContext context){
-
scanned.addDocumentation(resourceListing.scan(context));
-
}
ApiDocumentationScanner:resourceListing.scan(context)进行过滤出当前Docket的所有接口
-
publicDocumentation scan(DocumentationContext context){
-
////获取当前Docket配置的包路径下的RequestHandler,并按照Controller分成组
-
ApiListingReferenceScanResult result = apiListingReferenceScanner.scan(context);
-
//获取分组列表信息
-
ApiListingScanningContext listingContext =newApiListingScanningContext(context,
-
result.getResourceGroupRequestMappings());
-
//对每组中的接口进行排序以及初始化host、protocols、apiVersion等信息,此时会对调用插件ApiListingBuilderPlugin
-
Multimap<String,ApiListing> apiListings = apiListingScanner.scan(listingContext);
-
//获取每组的标签
-
Set<Tag> tags = toTags(apiListings);
-
tags.addAll(context.getTags());
-
//设置基本信息
-
DocumentationBuilder group =newDocumentationBuilder()
-
.name(context.getGroupName())
-
.apiListingsByResourceGroupName(apiListings)
-
.produces(context.getProduces())
-
.consumes(context.getConsumes())
-
.host(context.getHost())
-
.schemes(context.getProtocols())
-
.basePath(context.getPathProvider().getApplicationBasePath())
-
.tags(tags);
-
Set<ApiListingReference> apiReferenceSet = newTreeSet(listingReferencePathComparator());
-
apiReferenceSet.addAll(apiListingReferences(apiListings, context));
-
ResourceListing resourceListing =newResourceListingBuilder()
-
.apiVersion(context.getApiInfo().getVersion())
-
.apis(from(apiReferenceSet).toSortedList(context.getListingReferenceOrdering()))
-
.securitySchemes(context.getSecuritySchemes())
-
.info(context.getApiInfo())
-
.build();
-
group.resourceListing(resourceListing);
-
return group.build();
-
}
ApiListingReferenceScanner:scan()获取当前Docket配置的包路径下的requestMapping,并按照Controller分成组
-
publicApiListingReferenceScanResult scan(DocumentationContext context){
-
LOG.info("Scanning for api listing references");
-
ArrayListMultimap<ResourceGroup,RequestMappingContext> resourceGroupRequestMappings
-
=ArrayListMultimap.create();
-
//获取Api选择器,ApiSelector中存放了当前docket需要扫描的请求包路径
-
ApiSelector selector = context.getApiSelector();
-
//过滤出当前Docket配置包路径下的RequestHandler
-
Iterable<RequestHandler> matchingHandlers = from(context.getRequestHandlers())
-
.filter(selector.getRequestHandlerSelector());
-
//将同一个Controller下的接口分成同一组 handler.groupName()即是Controller类的名称
-
for(RequestHandler handler : matchingHandlers){
-
ResourceGroup resourceGroup =newResourceGroup(handler.groupName(),
-
handler.declaringClass(),0);
-
RequestMappingContext requestMappingContext
-
=newRequestMappingContext(context, handler);
-
resourceGroupRequestMappings.put(resourceGroup, requestMappingContext);
-
}
-
returnnewApiListingReferenceScanResult(asMap(resourceGroupRequestMappings));
-
}
ApiListingScanner:scan()获取每组资源(每个Controller)下每个接口,并为这些接口设置swagger的基本信息,pluginsManager.apiListing(apiListingContext)会调用注解中配置的插件,这些插件统一维护在DocumentationPluginsManager中
-
publicMultimap<String,ApiListing> scan(ApiListingScanningContext context){
-
/** 省略部分代码 */
-
apiListingMap.put(resourceGroup.getGroupName(), pluginsManager.apiListing(apiListingContext));
-
return apiListingMap;
-
}
用DocumentationPluginsManager中接口列表构建器插件ApiListingBuilderPlugin的实现类,解析每个接口上的swagger注解,并应用到接口上下文中
-
publicApiListing apiListing(ApiListingContext context){
-
for(ApiListingBuilderPlugin each : apiListingPlugins.getPluginsFor(context.getDocumentationType())){
-
//在此调用具体的插件实现类
-
each.apply(context);
-
}
-
return context.apiListingBuilder().build();
-
}
最后解析完所有接口后,将Docket缓存到内存中
-
privatevoid scanDocumentation(DocumentationContext context){
-
scanned.addDocumentation(resourceListing.scan(context));
-
}
-
publicclassDocumentationCache{
-
privateMap<String,Documentation> documentationLookup = newLinkedHashMap();
-
publicvoid addDocumentation(Documentation documentation){
-
documentationLookup.put(documentation.getGroupName(), documentation);
-
}
-
// ...
-
}
总结
通过Spring和Springfox的整合过程,可以了解到,依赖Spring框架开发某插件,可以依照以下几步来操作: (1)定义一个引导类,实现生命周期接口SmartLifecycle (2)在该引导类上添加注解@Component,并在启动Spring项目时,确保Spring能扫描到该引导类所在的包路径 (3)实现SmartLifecycle中的start()、stop()、isAutoStartup()、getPhase()等方法,可以参照DocumentationPluginsBootstrapper (4)在start()方法中初始化插件所需要执行的配置,并保存到Spring容器中或内存中 (5)项目启动后,可以直接从内存中或Spring容器中获取所需要的配置
推荐阅读
-
Springfox-Swagger源码分析
-
Mybatis源码分析——运行原理及流程
-
MySQL中Join算法实现原理分析[组图]_MySQL
-
thinkphp的where条件无法用数组,求分析原因
-
php中explode函数用法分析,phpexplode函数用法
-
详解python类实例分析
-
php抽象类用法实例分析,php抽象实例分析_PHP教程
-
PHP炸死你,源码,巨有效
-
ajax 同步和异步XMLHTTP代码分析
-
从ThinkPHP源码讲解_initialize与__construct construct 2 教程 construct 2 破解中文版 failed to initializ