Springfox-Swagger源码分析
Posted 算法技术猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springfox-Swagger源码分析相关的知识,希望对你有一定的参考价值。
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
public class SwaggerConfiguration{
...
}
EnableSwagger2注解
导入Swagger2DocumentationConfiguration注解的配置
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({Swagger2DocumentationConfiguration.class})
public @interface EnableSwagger2 {
}
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"
})
public class Swagger2DocumentationConfiguration {
@Bean
public JacksonModuleRegistrar swagger2Module() {
return new Swagger2JacksonModule();
}
}
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
})
public class SpringfoxWebMvcConfiguration {
//省略实现的代码
}
//DocumentationPluginsManager管理维护上述插件
@Component
public class DocumentationPluginsManager {
@Autowired
@Qualifier("documentationPluginRegistry")
private PluginRegistry<DocumentationPlugin, DocumentationType> documentationPlugins;
@Autowired
@Qualifier("apiListingBuilderPluginRegistry")
private PluginRegistry<ApiListingBuilderPlugin, DocumentationType> apiListingPlugins;
@Autowired
@Qualifier("parameterBuilderPluginRegistry")
private PluginRegistry<ParameterBuilderPlugin, DocumentationType> parameterPlugins;
@Autowired
@Qualifier("expandedParameterBuilderPluginRegistry")
private PluginRegistry<ExpandedParameterBuilderPlugin, DocumentationType> parameterExpanderPlugins;
@Autowired
@Qualifier("operationBuilderPluginRegistry")
private PluginRegistry<OperationBuilderPlugin, DocumentationType> operationBuilderPlugins;
@Autowired
@Qualifier("resourceGroupingStrategyRegistry")
private PluginRegistry<ResourceGroupingStrategy, DocumentationType> resourceGroupingStrategies;
@Autowired
@Qualifier("operationModelsProviderPluginRegistry")
private PluginRegistry<OperationModelsProviderPlugin, DocumentationType> operationModelsProviders;
@Autowired
@Qualifier("defaultsProviderPluginRegistry")
private PluginRegistry<DefaultsProviderPlugin, DocumentationType> defaultsProviders;
@Autowired
@Qualifier("pathDecoratorRegistry")
private PluginRegistry<PathDecorator, DocumentationContext> pathDecorators;
@Autowired(required = false)
@Qualifier("apiListingScannerPluginRegistry")
private PluginRegistry<ApiListingScannerPlugin, DocumentationType> apiListingScanners;
/** 省略部分代码 */
}
SwaggerCommonConfiguration
Spring会扫描配置包路径下的Bean
@Configuration
@ComponentScan(basePackages = {
"springfox.documentation.swagger.schema",
"springfox.documentation.swagger.readers",
"springfox.documentation.swagger.web"
})
public class SwaggerCommonConfiguration {
}
Spring创建并加载Swagger的引导类
DocumentationPluginsBootstrapper引导类作为Springfox的入口,需先由Spring创建并加载到Spring容器中
Spring整合Springfox流程
启动SpringBoot后,在刷新应用程序上下文时,会检索所有可用的、已经创建好单例的Lifecycle beans,以及检索所有的SmartLifecycle beans,并启动这些生命周期Bean
源码分析
SpringApplication源码
public ConfigurableApplicationContext run(String... args) {
/** 省略部分代码 */
//刷新应用程序上下文
this.refreshContext(context);
/** 省略部分代码 */
}
AbstractApplicationContext.refresh():Spring在finishBeanFactoryInitialization(beanFactory)方法实例化所有扫描到的单例
@Override
public void refresh() throws BeansException, 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.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
/** 省略部分代码 */
// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}
DefaultListableBeanFactory中遍历所有单例来创建bean实例,包含Springfox的引导类DocumentationPluginsBootstrapper、Swagger配置的Docket Bean等。创建好所有的Bean后,执行刷新操作,刷新即启动一些生命周期的Bean,DefaultLifecycleProcessor源码如下
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Last step: publish corresponding event. 发布相应的事件,具体刷新操作在这里面执行
finishRefresh();
}
/** 省略部分代码 */
}
protected void finishRefresh() {
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first. 刷新LifecycleBean
getLifecycleProcessor().onRefresh();
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
@Override
public void onRefresh() {
startBeans(true);
this.running = true;
}
DefaultLifecycleProcessor启动生命周期Bean:先获取生命周期Bean,然后再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true
private void startBeans(boolean autoStartupOnly) {
//获取生命周期Bean
Map<String, Lifecycle> lifecycleBeans = getLifecycleBeans();
//创建Map来维护LifecycleGroup
Map<Integer, LifecycleGroup> phases = new HashMap<Integer, LifecycleGroup>();
for (Map.Entry<String, ? extends Lifecycle> entry : lifecycleBeans.entrySet()) {
Lifecycle bean = entry.getValue();
//再次判断SmartLifecycle是否是当前Bean的超类,并且当前Bean中的isAutoStartup()方法是否返回true
if (!autoStartupOnly || (bean instanceof SmartLifecycle && ((SmartLifecycle) bean).isAutoStartup())) {
//判断当前Bean是否继承Phased,如果是,则返回该Bean的相对值
int phase = getPhase(bean);
//LifecycleGroup用于维护一组Lifecycle bean
LifecycleGroup group = phases.get(phase);
if (group == null) {
//创建一组LifecycleGroup
group = new LifecycleGroup(phase, this.timeoutPerShutdownPhase, lifecycleBeans, autoStartupOnly);
//保存LifecycleGroup
phases.put(phase, group);
}
//将当前bean存入LifecycleGroup
group.add(entry.getKey(), bean);
}
}
if (!phases.isEmpty()) {
List<Integer> keys = new ArrayList<Integer>(phases.keySet());
Collections.sort(keys);
for (Integer key : keys) {
//启动bean
phases.get(key).start();
}
}
}
protected Map<String, Lifecycle> getLifecycleBeans() {
Map<String, Lifecycle> beans = new LinkedHashMap<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;
}
protected int getPhase(Lifecycle bean) {
return (bean instanceof Phased ? ((Phased) bean).getPhase() : 0);
}
//在Bean中自定义的
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
至此,Spring整合Springfox就结束了,接下来是Spring刷新上下文是,初始化Springfox的引导类DocumentationPluginsBootstrapper,并进行相应的配置
启动Swagger引导类
引导类DocumentationPluginsBootstrapper是Springfox的入口,Swagger的所有配置初始化都是在里面进行
DocumentationPluginsBootstrapper启动流程图
源码分析
DocumentationPluginsBootstrapper.start()
@Override
public void 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中
private DocumentationContext buildContext(DocumentationPlugin each) {
return each.configure(defaultContextBuilder(each));
}
private DocumentationContextBuilder 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的多个插件
private void scanDocumentation(DocumentationContext context) {
scanned.addDocumentation(resourceListing.scan(context));
}
ApiDocumentationScanner:resourceListing.scan(context)进行过滤出当前Docket的所有接口
public Documentation scan(DocumentationContext context) {
////获取当前Docket配置的包路径下的RequestHandler,并按照Controller分成组
ApiListingReferenceScanResult result = apiListingReferenceScanner.scan(context);
//获取分组列表信息
ApiListingScanningContext listingContext = new ApiListingScanningContext(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 = new DocumentationBuilder()
.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 = new ResourceListingBuilder()
.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分成组
public ApiListingReferenceScanResult 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 = new ResourceGroup(handler.groupName(),
handler.declaringClass(), 0);
RequestMappingContext requestMappingContext
= new RequestMappingContext(context, handler);
resourceGroupRequestMappings.put(resourceGroup, requestMappingContext);
}
return new ApiListingReferenceScanResult(asMap(resourceGroupRequestMappings));
}
ApiListingScanner:scan()获取每组资源(每个Controller)下每个接口,并为这些接口设置swagger的基本信息,pluginsManager.apiListing(apiListingContext)会调用注解中配置的插件,这些插件统一维护在DocumentationPluginsManager中
public Multimap<String, ApiListing> scan(ApiListingScanningContext context) {
/** 省略部分代码 */
apiListingMap.put(resourceGroup.getGroupName(), pluginsManager.apiListing(apiListingContext));
return apiListingMap;
}
用DocumentationPluginsManager中接口列表构建器插件ApiListingBuilderPlugin的实现类,解析每个接口上的swagger注解,并应用到接口上下文中
public ApiListing apiListing(ApiListingContext context) {
for (ApiListingBuilderPlugin each : apiListingPlugins.getPluginsFor(context.getDocumentationType())) {
//在此调用具体的插件实现类
each.apply(context);
}
return context.apiListingBuilder().build();
}
最后解析完所有接口后,将Docket缓存到内存中
private void scanDocumentation(DocumentationContext context) {
scanned.addDocumentation(resourceListing.scan(context));
}
public class DocumentationCache {
private Map<String, Documentation> documentationLookup = newLinkedHashMap();
public void 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源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段
Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段
mysql jdbc源码分析片段 和 Tomcat's JDBC Pool
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段
Android 逆向ART 脱壳 ( DexClassLoader 脱壳 | DexClassLoader 构造函数 | 参考 Dalvik 的 DexClassLoader 类加载流程 )(代码片段