Springfox-Swagger源码分析

Posted 算法技术猿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springfox-Swagger源码分析相关的知识,希望对你有一定的参考价值。

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. public class SwaggerConfiguration{

  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 @interface EnableSwagger2 {

  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. public class Swagger2DocumentationConfiguration {

  9.  @Bean

  10.  public JacksonModuleRegistrar swagger2Module() {

  11.    return new Swagger2JacksonModule();

  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. public class SpringfoxWebMvcConfiguration {

  21.    //省略实现的代码

  22. }

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

  24. @Component

  25. public class DocumentationPluginsManager {

  26.  @Autowired

  27.  @Qualifier("documentationPluginRegistry")

  28.  private PluginRegistry<DocumentationPlugin, DocumentationType> documentationPlugins;

  29.  @Autowired

  30.  @Qualifier("apiListingBuilderPluginRegistry")

  31.  private PluginRegistry<ApiListingBuilderPlugin, DocumentationType> apiListingPlugins;

  32.  @Autowired

  33.  @Qualifier("parameterBuilderPluginRegistry")

  34.  private PluginRegistry<ParameterBuilderPlugin, DocumentationType> parameterPlugins;

  35.  @Autowired

  36.  @Qualifier("expandedParameterBuilderPluginRegistry")

  37.  private PluginRegistry<ExpandedParameterBuilderPlugin, DocumentationType> parameterExpanderPlugins;

  38.  @Autowired

  39.  @Qualifier("operationBuilderPluginRegistry")

  40.  private PluginRegistry<OperationBuilderPlugin, DocumentationType> operationBuilderPlugins;

  41.  @Autowired

  42.  @Qualifier("resourceGroupingStrategyRegistry")

  43.  private PluginRegistry<ResourceGroupingStrategy, DocumentationType> resourceGroupingStrategies;

  44.  @Autowired

  45.  @Qualifier("operationModelsProviderPluginRegistry")

  46.  private PluginRegistry<OperationModelsProviderPlugin, DocumentationType> operationModelsProviders;

  47.  @Autowired

  48.  @Qualifier("defaultsProviderPluginRegistry")

  49.  private PluginRegistry<DefaultsProviderPlugin, DocumentationType> defaultsProviders;

  50.  @Autowired

  51.  @Qualifier("pathDecoratorRegistry")

  52.  private PluginRegistry<PathDecorator, DocumentationContext> pathDecorators;

  53.  @Autowired(required = false)

  54.  @Qualifier("apiListingScannerPluginRegistry")

  55.  private PluginRegistry<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. public class SwaggerCommonConfiguration {

  8. }

Spring创建并加载Swagger的引导类

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

Spring整合Springfox流程

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

源码分析

SpringApplication源码

 
   
   
 
  1. public ConfigurableApplicationContext run(String... args) {

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

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

  4.   this.refreshContext(context);

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

  6. }

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

 
   
   
 
  1. @Override

  2. public void refresh() throws BeansException, 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. protected void 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. public void refresh() throws BeansException, IllegalStateException {

  3.   synchronized (this.startupShutdownMonitor) {

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

  5.         finishRefresh();

  6.      }

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

  8. }

  9. protected void 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(new ContextRefreshedEvent(this));

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

  17.   LiveBeansView.registerApplicationContext(this);

  18. }

  19. @Override

  20. public void onRefresh() {

  21.   startBeans(true);

  22.   this.running = true;

  23. }

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

 
   
   
 
  1. private void startBeans(boolean autoStartupOnly) {

  2.    //获取生命周期Bean

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

  4.    //创建Map来维护LifecycleGroup

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

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

  7.      Lifecycle bean = entry.getValue();

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

  9.      if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((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 = new LifecycleGroup(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 = new ArrayList<Integer>(phases.keySet());

  26.      Collections.sort(keys);

  27.      for (Integer key : keys) {

  28.        //启动bean

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

  30.      }

  31.   }

  32. }

  33. protected Map<String, Lifecycle> getLifecycleBeans() {

  34.   Map<String, Lifecycle> beans = new LinkedHashMap<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. protected int getPhase(Lifecycle bean) {

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

  57. }

  58. //在Bean中自定义的

  59. @Override

  60. public int getPhase() {

  61.  return Integer.MAX_VALUE;

  62. }

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

启动Swagger引导类

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

DocumentationPluginsBootstrapper启动流程图

源码分析

DocumentationPluginsBootstrapper.start()

 
   
   
 
  1. @Override

  2. public void 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. private DocumentationContext buildContext(DocumentationPlugin each) {

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

  3. }

  4. private DocumentationContextBuilder 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. private void scanDocumentation(DocumentationContext context) {

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

  3. }

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

 
   
   
 
  1. public Documentation scan(DocumentationContext context) {

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

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

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

  5.  ApiListingScanningContext listingContext = new ApiListingScanningContext(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 = new DocumentationBuilder()

  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 = new ResourceListingBuilder()

  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. public ApiListingReferenceScanResult 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 = new ResourceGroup(handler.groupName(),

  13.        handler.declaringClass(), 0);

  14.    RequestMappingContext requestMappingContext

  15.        = new RequestMappingContext(context, handler);

  16.    resourceGroupRequestMappings.put(resourceGroup, requestMappingContext);

  17.  }

  18.  return new ApiListingReferenceScanResult(asMap(resourceGroupRequestMappings));

  19. }

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

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

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

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

  4.  return apiListingMap;

  5. }

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

 
   
   
 
  1. public ApiListing 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. private void scanDocumentation(DocumentationContext context) {

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

  3. }

  4. public class DocumentationCache {

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

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


以上是关于Springfox-Swagger源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

《Docker 源码分析》全球首发啦!

mysql jdbc源码分析片段 和 Tomcat's JDBC Pool

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段

Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段