Springfox-Swagger源码分析

微信公众号文章列表:关注公众号(coding_song)阅读更清晰,附件为微信二维码

原文链接:https://mp.weixin.qq.com/s/c3uU_t2pejZe4ybQ-wVcOg

Swagger依赖

  1. <dependency>

  2.    <groupId>io.springfox</groupId>

  3.    <artifactId>springfox-swagger2</artifactId>

  4.    <version>2.6.1</version>

  5. </dependency>

  6. <dependency>

  7.    <groupId>io.springfox</groupId>

  8.    <artifactId>springfox-swagger-ui</artifactId>

  9.    <version>2.6.1</version>

  10. </dependency>

EnableSwagger2配置

通过注解EnableSwagger2导入spring需要扫描的包路径

  1. @Configuration

  2. @EnableSwagger2

  3. publicclassSwaggerConfiguration{

  4.    ...

  5. }

EnableSwagger2注解

导入Swagger2DocumentationConfiguration注解的配置

  1. @Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)

  2. @Target(value ={ java.lang.annotation.ElementType.TYPE })

  3. @Documented

  4. @Import({Swagger2DocumentationConfiguration.class})

  5. public@interfaceEnableSwagger2{

  6. }

Swagger2DocumentationConfiguration

导入配置类SpringfoxWebMvcConfiguration和SwaggerCommonConfiguration,Spring将扫描这两个类中配置的包路径下的类

  1. @Configuration

  2. @Import({SpringfoxWebMvcConfiguration.class,SwaggerCommonConfiguration.class})

  3. @ComponentScan(basePackages ={

  4.    "springfox.documentation.swagger2.readers.parameter",

  5.    "springfox.documentation.swagger2.web",

  6.    "springfox.documentation.swagger2.mappers"

  7. })

  8. publicclassSwagger2DocumentationConfiguration{

  9.  @Bean

  10.  publicJacksonModuleRegistrar swagger2Module(){

  11.    returnnewSwagger2JacksonModule();

  12.  }

  13. }

SpringfoxWebMvcConfiguration

添加扫描的包,并按照顺序依次注册相应的插件,这些插件统一维护在DocumentationPluginsManager中

  1. @Configuration

  2. @Import({ModelsConfiguration.class})

  3. @ComponentScan(basePackages ={

  4.    "springfox.documentation.spring.web.scanners",

  5.    "springfox.documentation.spring.web.readers.operation",

  6.    "springfox.documentation.spring.web.readers.parameter",

  7.    "springfox.documentation.spring.web.plugins",

  8.    "springfox.documentation.spring.web.paths"

  9. })

  10. @EnablePluginRegistries({DocumentationPlugin.class,

  11.    ApiListingBuilderPlugin.class,

  12.    OperationBuilderPlugin.class,

  13.    ParameterBuilderPlugin.class,

  14.    ExpandedParameterBuilderPlugin.class,

  15.    ResourceGroupingStrategy.class,

  16.    OperationModelsProviderPlugin.class,

  17.    DefaultsProviderPlugin.class,

  18.    PathDecorator.class

  19. })

  20. publicclassSpringfoxWebMvcConfiguration{

  21.    //省略实现的代码

  22. }

  23. //DocumentationPluginsManager管理维护上述插件

  24. @Component

  25. publicclassDocumentationPluginsManager{

  26.  @Autowired

  27.  @Qualifier("documentationPluginRegistry")

  28.  privatePluginRegistry<DocumentationPlugin,DocumentationType> documentationPlugins;

  29.  @Autowired

  30.  @Qualifier("apiListingBuilderPluginRegistry")

  31.  privatePluginRegistry<ApiListingBuilderPlugin,DocumentationType> apiListingPlugins;

  32.  @Autowired

  33.  @Qualifier("parameterBuilderPluginRegistry")

  34.  privatePluginRegistry<ParameterBuilderPlugin,DocumentationType> parameterPlugins;

  35.  @Autowired

  36.  @Qualifier("expandedParameterBuilderPluginRegistry")

  37.  privatePluginRegistry<ExpandedParameterBuilderPlugin,DocumentationType> parameterExpanderPlugins;

  38.  @Autowired

  39.  @Qualifier("operationBuilderPluginRegistry")

  40.  privatePluginRegistry<OperationBuilderPlugin,DocumentationType> operationBuilderPlugins;

  41.  @Autowired

  42.  @Qualifier("resourceGroupingStrategyRegistry")

  43.  privatePluginRegistry<ResourceGroupingStrategy,DocumentationType> resourceGroupingStrategies;

  44.  @Autowired

  45.  @Qualifier("operationModelsProviderPluginRegistry")

  46.  privatePluginRegistry<OperationModelsProviderPlugin,DocumentationType> operationModelsProviders;

  47.  @Autowired

  48.  @Qualifier("defaultsProviderPluginRegistry")

  49.  privatePluginRegistry<DefaultsProviderPlugin,DocumentationType> defaultsProviders;

  50.  @Autowired

  51.  @Qualifier("pathDecoratorRegistry")

  52.  privatePluginRegistry<PathDecorator,DocumentationContext> pathDecorators;

  53.  @Autowired(required =false)

  54.  @Qualifier("apiListingScannerPluginRegistry")

  55.  privatePluginRegistry<ApiListingScannerPlugin,DocumentationType> apiListingScanners;

  56. /** 省略部分代码 */

  57. }

SwaggerCommonConfiguration

Spring会扫描配置包路径下的Bean

  1. @Configuration

  2. @ComponentScan(basePackages ={

  3.    "springfox.documentation.swagger.schema",

  4.    "springfox.documentation.swagger.readers",

  5.    "springfox.documentation.swagger.web"

  6. })

  7. publicclassSwaggerCommonConfiguration{

  8. }

Spring创建并加载Swagger的引导类

DocumentationPluginsBootstrapper引导类作为Springfox的入口,需先由Spring创建并加载到Spring容器中

Spring整合Springfox流程

启动SpringBoot后,在刷新应用程序上下文时,会检索所有可用的、已经创建好单例的Lifecycle beans,以及检索所有的SmartLifecycle beans,并启动这些生命周期Bean

源码分析

SpringApplication源码

  1. publicConfigurableApplicationContext run(String... args){

  2.   /** 省略部分代码 */

  3.   //刷新应用程序上下文

  4.   this.refreshContext(context);

  5.   /** 省略部分代码 */

  6. }

AbstractApplicationContext.refresh():Spring在finishBeanFactoryInitialization(beanFactory)方法实例化所有扫描到的单例

  1. @Override

  2. publicvoid refresh()throwsBeansException,IllegalStateException{

  3.   synchronized(this.startupShutdownMonitor){

  4.         /** 省略部分代码 */

  5.         // Instantiate all remaining (non-lazy-init) singletons. 实例化所有剩余的非懒初始化的单例

  6.         finishBeanFactoryInitialization(beanFactory);

  7.         // Last step: publish corresponding event. 发布相应的事件,具体刷新操作在这里面执行

  8.         finishRefresh();

  9.      }

  10.     /** 省略部分代码 */

  11. }

  12. /**

  13. * Finish the initialization of this context's bean factory,

  14. * initializing all remaining singleton beans.

  15. */

  16. protectedvoid finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory){

  17.    /** 省略部分代码 */

  18.   // Instantiate all remaining (non-lazy-init) singletons.

  19.   beanFactory.preInstantiateSingletons();

  20. }

DefaultListableBeanFactory中遍历所有单例来创建bean实例,包含Springfox的引导类DocumentationPluginsBootstrapper、Swagger配置的Docket Bean等。创建好所有的Bean后,执行刷新操作,刷新即启动一些生命周期的Bean,DefaultLifecycleProcessor源码如下

  1. @Override

  2. publicvoid refresh()throwsBeansException,IllegalStateException{

  3.   synchronized(this.startupShutdownMonitor){

  4.         // Last step: publish corresponding event. 发布相应的事件,具体刷新操作在这里面执行

  5.         finishRefresh();

  6.      }

  7.     /** 省略部分代码 */

  8. }

  9. protectedvoid finishRefresh(){

  10.   // Initialize lifecycle processor for this context.

  11.   initLifecycleProcessor();

  12.   // Propagate refresh to lifecycle processor first. 刷新LifecycleBean

  13.   getLifecycleProcessor().onRefresh();

  14.   // Publish the final event.

  15.   publishEvent(newContextRefreshedEvent(this));

  16.   // Participate in LiveBeansView MBean, if active.

  17.   LiveBeansView.registerApplicationContext(this);

  18. }

  19. @Override

  20. publicvoid onRefresh(){

  21.   startBeans(true);

  22.   this.running =true;

  23. }

DefaultLifecycleProcessor启动生命周期Bean:先获取生命周期Bean,然后再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true

  1. privatevoid startBeans(boolean autoStartupOnly){

  2.    //获取生命周期Bean

  3.   Map<String,Lifecycle> lifecycleBeans = getLifecycleBeans();

  4.    //创建Map来维护LifecycleGroup

  5.   Map<Integer,LifecycleGroup> phases =newHashMap<Integer,LifecycleGroup>();

  6.   for(Map.Entry<String,?extendsLifecycle> entry : lifecycleBeans.entrySet()){

  7.      Lifecycle bean = entry.getValue();

  8.      //再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true

  9.      if(!autoStartupOnly ||(bean instanceofSmartLifecycle&&((SmartLifecycle) bean).isAutoStartup())){

  10.        //判断当前Bean是否继承Phased,如果是,则返回该Bean的相对值

  11.         int phase = getPhase(bean);

  12.        //LifecycleGroup用于维护一组Lifecycle bean

  13.         LifecycleGroup group = phases.get(phase);

  14.         if(group ==null){

  15.            //创建一组LifecycleGroup

  16.            group =newLifecycleGroup(phase,this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);

  17.            //保存LifecycleGroup

  18.            phases.put(phase, group);

  19.         }

  20.        //将当前bean存入LifecycleGroup

  21.         group.add(entry.getKey(), bean);

  22.      }

  23.   }

  24.   if(!phases.isEmpty()){

  25.      List<Integer> keys =newArrayList<Integer>(phases.keySet());

  26.      Collections.sort(keys);

  27.      for(Integer key : keys){

  28.        //启动bean

  29.         phases.get(key).start();

  30.      }

  31.   }

  32. }

  33. protectedMap<String,Lifecycle> getLifecycleBeans(){

  34.   Map<String,Lifecycle> beans =newLinkedHashMap<String,Lifecycle>();

  35.   //获取所有继承Lifecycle类的、符合条件的子类名称

  36.   String[] beanNames =this.beanFactory.getBeanNamesForType(Lifecycle.class,false,false);

  37.   for(String beanName : beanNames){

  38.      //Bean名称

  39.      String beanNameToRegister =BeanFactoryUtils.transformedBeanName(beanName);

  40.      //判断当前Bean名称对应的实例,是否是FactoryBean

  41.      boolean isFactoryBean =this.beanFactory.isFactoryBean(beanNameToRegister);

  42.      String beanNameToCheck =(isFactoryBean ?BeanFactory.FACTORY_BEAN_PREFIX + beanName : beanName);

  43.      //如果beanFactory有当前bean的实例、且Lifecycle或SmartLifecycle是当前bean的超类,则获取当前beanName的实例,将其存放到Map中返回

  44.      if((this.beanFactory.containsSingleton(beanNameToRegister)&&

  45.            (!isFactoryBean ||Lifecycle.class.isAssignableFrom(this.beanFactory.getType(beanNameToCheck))))||

  46.            SmartLifecycle.class.isAssignableFrom(this.beanFactory.getType(beanNameToCheck))){

  47.         Lifecycle bean =this.beanFactory.getBean(beanNameToCheck,Lifecycle.class);

  48.         if(bean !=this){

  49.            beans.put(beanNameToRegister, bean);

  50.         }

  51.      }

  52.   }

  53.   return beans;

  54. }

  55. protectedint getPhase(Lifecycle bean){

  56.   return(bean instanceofPhased?((Phased) bean).getPhase():0);

  57. }

  58. //在Bean中自定义的

  59. @Override

  60. publicint getPhase(){

  61.  returnInteger.MAX_VALUE;

  62. }

至此,Spring整合Springfox就结束了,接下来是Spring刷新上下文是,初始化Springfox的引导类DocumentationPluginsBootstrapper,并进行相应的配置

启动Swagger引导类

引导类DocumentationPluginsBootstrapper是Springfox的入口,Swagger的所有配置初始化都是在里面进行

DocumentationPluginsBootstrapper启动流程图

源码分析

DocumentationPluginsBootstrapper.start()

  1. @Override

  2. publicvoid start(){

  3.  if(initialized.compareAndSet(false,true)){

  4.    log.info("Context refreshed");

  5.    // 获取所有的Docket实例

  6.    List<DocumentationPlugin> plugins = pluginOrdering().sortedCopy(documentationPluginsManager.documentationPlugins());

  7.    log.info("Found {} custom documentation plugin(s)", plugins.size());

  8.    //遍历每个Docket

  9.    for(DocumentationPlugin each : plugins){

  10.      DocumentationType documentationType = each.getDocumentationType();

  11.      if(each.isEnabled()){

  12.        //初始化Docket配置,并保存到内存中

  13.        scanDocumentation(buildContext(each));

  14.      }else{

  15.        log.info("Skipping initializing disabled plugin bean {} v{}",

  16.            documentationType.getName(), documentationType.getVersion());

  17.      }

  18.    }

  19.  }

  20. }

首先先组装Docket基本信息,获取项目所有RequestMapping的接口将其放到DocumentationContextBuilder中

  1. privateDocumentationContext buildContext(DocumentationPlugin each){

  2.  return each.configure(defaultContextBuilder(each));

  3. }

  4. privateDocumentationContextBuilder defaultContextBuilder(DocumentationPlugin each){

  5.  DocumentationType documentationType = each.getDocumentationType();

  6.  //获取所有RequestMapping的接口

  7.  List<RequestHandler> requestHandlers = from(handlerProviders)

  8.      .transformAndConcat(handlers())

  9.      .toList();

  10. //将所有请求接口放到DocumentationContextBuilder中

  11.  return documentationPluginsManager

  12.      .createContextBuilder(documentationType, defaultConfiguration)

  13.      .requestHandlers(requestHandlers);

  14. }

装好Docket后,然后过滤出当前Docket配置的包路径下的接口,Docket有一个插件管理器DocumentationPluginsManager用于维护Docket的多个插件

  1. privatevoid scanDocumentation(DocumentationContext context){

  2.  scanned.addDocumentation(resourceListing.scan(context));

  3. }

ApiDocumentationScanner:resourceListing.scan(context)进行过滤出当前Docket的所有接口

  1. publicDocumentation scan(DocumentationContext context){

  2.  ////获取当前Docket配置的包路径下的RequestHandler,并按照Controller分成组

  3.  ApiListingReferenceScanResult result = apiListingReferenceScanner.scan(context);

  4.  //获取分组列表信息

  5.  ApiListingScanningContext listingContext =newApiListingScanningContext(context,

  6.      result.getResourceGroupRequestMappings());

  7.  //对每组中的接口进行排序以及初始化host、protocols、apiVersion等信息,此时会对调用插件ApiListingBuilderPlugin

  8.  Multimap<String,ApiListing> apiListings = apiListingScanner.scan(listingContext);

  9.  //获取每组的标签

  10.  Set<Tag> tags = toTags(apiListings);

  11.  tags.addAll(context.getTags());

  12.  //设置基本信息

  13.  DocumentationBuilder group =newDocumentationBuilder()

  14.      .name(context.getGroupName())

  15.      .apiListingsByResourceGroupName(apiListings)

  16.      .produces(context.getProduces())

  17.      .consumes(context.getConsumes())

  18.      .host(context.getHost())

  19.      .schemes(context.getProtocols())

  20.      .basePath(context.getPathProvider().getApplicationBasePath())

  21.      .tags(tags);

  22.  Set<ApiListingReference> apiReferenceSet = newTreeSet(listingReferencePathComparator());

  23.  apiReferenceSet.addAll(apiListingReferences(apiListings, context));

  24.  ResourceListing resourceListing =newResourceListingBuilder()

  25.      .apiVersion(context.getApiInfo().getVersion())

  26.      .apis(from(apiReferenceSet).toSortedList(context.getListingReferenceOrdering()))

  27.      .securitySchemes(context.getSecuritySchemes())

  28.      .info(context.getApiInfo())

  29.      .build();

  30.  group.resourceListing(resourceListing);

  31.  return group.build();

  32. }

ApiListingReferenceScanner:scan()获取当前Docket配置的包路径下的requestMapping,并按照Controller分成组

  1. publicApiListingReferenceScanResult scan(DocumentationContext context){

  2.  LOG.info("Scanning for api listing references");

  3.  ArrayListMultimap<ResourceGroup,RequestMappingContext> resourceGroupRequestMappings

  4.      =ArrayListMultimap.create();

  5.  //获取Api选择器,ApiSelector中存放了当前docket需要扫描的请求包路径

  6.  ApiSelector selector = context.getApiSelector();

  7. //过滤出当前Docket配置包路径下的RequestHandler

  8.  Iterable<RequestHandler> matchingHandlers = from(context.getRequestHandlers())

  9.      .filter(selector.getRequestHandlerSelector());

  10.  //将同一个Controller下的接口分成同一组 handler.groupName()即是Controller类的名称

  11.  for(RequestHandler handler : matchingHandlers){

  12.    ResourceGroup resourceGroup =newResourceGroup(handler.groupName(),

  13.        handler.declaringClass(),0);

  14.    RequestMappingContext requestMappingContext

  15.        =newRequestMappingContext(context, handler);

  16.    resourceGroupRequestMappings.put(resourceGroup, requestMappingContext);

  17.  }

  18.  returnnewApiListingReferenceScanResult(asMap(resourceGroupRequestMappings));

  19. }

ApiListingScanner:scan()获取每组资源(每个Controller)下每个接口,并为这些接口设置swagger的基本信息,pluginsManager.apiListing(apiListingContext)会调用注解中配置的插件,这些插件统一维护在DocumentationPluginsManager中

  1. publicMultimap<String,ApiListing> scan(ApiListingScanningContext context){

  2.    /** 省略部分代码  */

  3.    apiListingMap.put(resourceGroup.getGroupName(), pluginsManager.apiListing(apiListingContext));

  4.  return apiListingMap;

  5. }

用DocumentationPluginsManager中接口列表构建器插件ApiListingBuilderPlugin的实现类,解析每个接口上的swagger注解,并应用到接口上下文中

  1. publicApiListing apiListing(ApiListingContext context){

  2.  for(ApiListingBuilderPlugin each : apiListingPlugins.getPluginsFor(context.getDocumentationType())){

  3.    //在此调用具体的插件实现类

  4.    each.apply(context);

  5.  }

  6.  return context.apiListingBuilder().build();

  7. }

最后解析完所有接口后,将Docket缓存到内存中

  1. privatevoid scanDocumentation(DocumentationContext context){

  2.  scanned.addDocumentation(resourceListing.scan(context));

  3. }

  4. publicclassDocumentationCache{

  5.  privateMap<String,Documentation> documentationLookup = newLinkedHashMap();

  6.  publicvoid addDocumentation(Documentation documentation){

  7.    documentationLookup.put(documentation.getGroupName(), documentation);

  8.  }

  9. // ...

  10. }

总结

通过Spring和Springfox的整合过程,可以了解到,依赖Spring框架开发某插件,可以依照以下几步来操作: (1)定义一个引导类,实现生命周期接口SmartLifecycle (2)在该引导类上添加注解@Component,并在启动Spring项目时,确保Spring能扫描到该引导类所在的包路径 (3)实现SmartLifecycle中的start()、stop()、isAutoStartup()、getPhase()等方法,可以参照DocumentationPluginsBootstrapper (4)在start()方法中初始化插件所需要执行的配置,并保存到Spring容器中或内存中 (5)项目启动后,可以直接从内存中或Spring容器中获取所需要的配置

猜你喜欢

转载自itxiaojiang.iteye.com/blog/2434560