欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Springfox-Swagger源码分析

程序员文章站 2023-12-23 11:32:52
...

微信公众号文章列表:关注公众号(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

Springfox-Swagger源码分析
            
    
    博客分类: Swagger Swagger 

源码分析

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启动流程图

Springfox-Swagger源码分析
            
    
    博客分类: Swagger Swagger 

源码分析

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容器中获取所需要的配置

 

相关标签: Swagger

上一篇:

下一篇: