Springfox swagger2 源码解析
doc访问页面 http://localhost:8080/doc.html
api-json访问页面 http://localhost:8080/v2/api-docs
pom依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
找starter的启动配置文件spring.factories文件找到启动类,做了一些bean的配置。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.github.xiaoymin.knife4j.spring.configuration.Knife4jAutoConfiguration
根据路径/v2/api-docs顺藤摸瓜 找到 Swagger2ControllerWebMvc
@RequestMapping(
method = RequestMethod.GET,
produces = {APPLICATION_JSON_VALUE, HAL_MEDIA_TYPE})
public ResponseEntity<Json> getDocumentation(
@RequestParam(value = "group", required = false) String swaggerGroup,
HttpServletRequest servletRequest) {
String groupName = ofNullable(swaggerGroup).orElse(Docket.DEFAULT_GROUP_NAME);
Documentation documentation = documentationCache.documentationByGroup(groupName);
if (documentation == null) {
LOGGER.warn("Unable to find specification for group {}", groupName);
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
Swagger swagger = mapper.mapDocumentation(documentation);
SwaggerTransformationContext<HttpServletRequest> context
= new SwaggerTransformationContext<>(swagger, servletRequest);
List<WebMvcSwaggerTransformationFilter> filters = transformations.getPluginsFor(DocumentationType.SWAGGER_2);
for (WebMvcSwaggerTransformationFilter each : filters) {
context = context.next(each.transform(context));
}
return new ResponseEntity<>(jsonSerializer.toJson(context.getSpecification()), HttpStatus.OK);
}
}
从缓存中获取文档
Documentation documentation = documentationCache.documentationByGroup(groupName);
何时加载缓存文档呢?
由文档加载器DocumentationPluginsBootstrapper进行加载,类实现了SmartLifecycle接口,当spring加载完bean后,调用start()方法进行加载。
public class DocumentationPluginsBootstrapper
extends AbstractDocumentationPluginsBootstrapper
implements SmartLifecycle{
public DocumentationPluginsBootstrapper(
DocumentationPluginsManager documentationPluginsManager,
List<RequestHandlerProvider> handlerProviders,
DocumentationCache scanned,
ApiDocumentationScanner resourceListing,
TypeResolver typeResolver,
Defaults defaults,
PathProvider pathProvider,
Environment environment) {
super(
documentationPluginsManager,
handlerProviders,
scanned,
resourceListing,
defaults,
typeResolver,
pathProvider);
this.environment = environment;
}
// bean 加载完成后调用
public void start() {
if (initialized.compareAndSet(false, true)) {
LOGGER.debug("Documentation plugins bootstrapped");
super.bootstrapDocumentationPlugins();
}
}
}
Spring SmartLifecycle 在容器所有bean加载和初始化完毕执行
在使用Spring开发时,我们都知道,所有bean都交给Spring容器来统一管理,其中包括每一个bean的加载和初始化。
有时候我们需要在Spring加载和初始化所有bean后,接着执行一些任务或者启动需要的异步服务,这样我们可以使用 SmartLifecycle 来做到。
这个和 @PostConstruct、@PreDestroy 的bean的初始化和销毁方法不同,Bean生命周期级别和容器生命周期级别在应用场景上是有区别的。
SmartLifecycle 是一个接口。当Spring容器加载所有bean并完成初始化之后,会接着回调实现该接口的类中对应的方法(start()方法)。
文档插件管理器 DocumentationPluginsManager
此类以PluginRegistry的方式注入了一系列插件
documentationPlugins()方法调用了DocumentationPlugin 文档插件,若没有则生成一个默认的插件Docket,通常在Swagger2Configuration配置文件会注入一个Docket。
@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("operationModelsProviderPluginRegistry")
private PluginRegistry<OperationModelsProviderPlugin, DocumentationType> operationModelsProviders;
@Autowired
@Qualifier("defaultsProviderPluginRegistry")
private PluginRegistry<DefaultsProviderPlugin, DocumentationType> defaultsProviders;
@Autowired
@Qualifier("pathDecoratorRegistry")
private PluginRegistry<PathDecorator, DocumentationContext> pathDecorators;
@Autowired
@Qualifier("apiListingScannerPluginRegistry")
private PluginRegistry<ApiListingScannerPlugin, DocumentationType> apiListingScanners;
@Autowired
@Qualifier("responseBuilderPluginRegistry")
private PluginRegistry<ResponseBuilderPlugin, DocumentationType> responsePlugins;
@Autowired
@Qualifier("modelNamesRegistryFactoryPluginRegistry")
private PluginRegistry<ModelNamesRegistryFactoryPlugin, DocumentationType> modelNameRegistryFactoryPlugins;
public Collection<DocumentationPlugin> documentationPlugins() throws IllegalStateException {
List<DocumentationPlugin> plugins = documentationPlugins.getPlugins();
ensureNoDuplicateGroups(plugins);
if (plugins.isEmpty()) {
return singleton(defaultDocumentationPlugin());
}
return plugins;
}
例如 OperationBuilderPlugin
PluginRegistry<OperationBuilderPlugin, DocumentationType> operationBuilderPlugins
Control + H 查看其继承结构
栗子:
SwaggerResponseMessageReader实现了ResponseMessage的读取。read()方法从OperationContext上下文中获取ApiOperation、ResponseHeader、ApiResponse注解信息。
public class SwaggerResponseMessageReader implements OperationBuilderPlugin {
protected Compatibility<Set<ResponseMessage>, Set<Response>> read(OperationContext context) {}
}
PluginRegistry
Spring Plugin提供一个标准的Plugin接口供开发人员继承使用声明自己的插件机制,然后通过@EnablePluginRegistries注解依赖注入到Spring的容器中,Spring容器会为我们自动匹配到插件的所有实现子对象,最终我们在代码中使用时,通过依赖注入注解,注入PluginRegistry extends Plugin对象拿到插件实例进行操作。
项目中自定义swagger2Config配置文件,注入Docket对象,即一个DocumentationPlugin插件。
public class Docket implements DocumentationPlugin
@Configuration
public class Swagger2Config {
@Bean
public Docket createRestApi() {
List<Response> globalResponses = new ArrayList<>();
// 根据Enum构建了全局的Response
for (ResponseCodeEnums item : ResponseCodeEnums.values()) {
globalResponses.add(new ResponseBuilder()
.code(String.valueOf(item.getCode()))
.description(item.getDesc())
.build());
}
return new Docket(DocumentationType.OAS_30)
.useDefaultResponseMessages(true)
.globalResponses(HttpMethod.GET, globalResponses)
.globalResponses(HttpMethod.POST, globalResponses)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("com.dogs.doc.controller"))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Dogs APIs")
.description("knife4j")
.termsOfServiceUrl("")
.version("3.0")
.build();
}
springfox的配置文件SpringfoxWebConfiguration
EnablePluginRegistries启用插件
Defaults组件实现了一些默认的设置,如全局Response 200、401、403的返回信息。
@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,
ResponseBuilderPlugin.class,
ExpandedParameterBuilderPlugin.class,
OperationModelsProviderPlugin.class,
DefaultsProviderPlugin.class,
PathDecorator.class,
ApiListingScannerPlugin.class,
ModelNamesRegistryFactoryPlugin.class
})
public class SpringfoxWebConfiguration {
@Bean
public Defaults defaults() {
return new Defaults();
}
@Bean
public DocumentationCache resourceGroupCache() {
return new DocumentationCache();
}
@Bean
public JsonSerializer jsonSerializer(List<JacksonModuleRegistrar> moduleRegistrars) {
return new JsonSerializer(moduleRegistrars);
}
@Bean
public DescriptionResolver descriptionResolver(Environment environment) {
return new DescriptionResolver(environment);
}
@Bean
public HandlerMethodResolver methodResolver(TypeResolver resolver) {
return new HandlerMethodResolver(resolver);
}
@Bean
public PathProvider pathProvider() {
return new DefaultPathProvider();
}
}
总结:
swagger基于PluginRegistry的方式注册插件。 默认提供了配置类ApiInfo,用户可以自定义配置类,可配置的内容可以参考ApiInfo的属性。