Spring工程往往要最后形成文档,供其他人使用,尤其是开发的接口。Java中的类文档有很多种,比如:javadoc,genDoc,SwaggerUI等。按照要求,我们首先,需要对不同的Controller包进行分组,每个controller包都代表一类接口,其次,原有SwagerUI显示界面也许不是我们想要的,所以我们需要改造。
注意,我代码中用到的SwaggerUI版本是2.7.0,每个版本的SwaggerUI差异比较大。
我们需要对SwaggerUI进行全局配置,让他支持按照包动态生成DocketBean。 首先我们需要定义一个用于说明文档描述的类,用于自动化配置。
private class SwaggerConfigProperties { private String version; private Boolean enable; private String groupName; private String title; private String description; private String basePackage; public SwaggerConfigProperties(Environment env, String groupName) { this.groupName = env.getProperty("swagger2." + groupName + ".groupName", "groupName"); this.title = env.getProperty("swagger2." + groupName + ".title", "title"); this.description = env.getProperty("swagger2." + groupName + ".description", "description"); this.version = env.getProperty("swagger2." + groupName + ".version", "version"); this.basePackage = env.getProperty("swagger2." + groupName + ".basePackage", "basePackage"); this.enable = Boolean.parseBoolean(env.getProperty("swagger2.enable", "false")); } ///getter ///setter }该类对应于配置文件:
#启用/禁用swagger swagger2.enable=true # rest API 标题 #服务器启动后可以通过访问http://your ip:8090/api/swagger-ui.html查看发布的REST接口 swagger2.title=BFSAPP相关Restfull API接口 # group1 swagger2.group1.groupName=文档分组1 swagger2.group1.title=文档分组1文档 swagger2.group1.description=文档分组1 swagger2.group1.version=1.0 swagger2.group1.basePackage=com.xxx.group1 # 这里是第一个controller组对应的包名 # group2 swagger2.group2.groupName=文档分组2 swagger2.group2.title=文档分组1文档 swagger2.group2.description=文档分组2 swagger2.group2.version=1.0 swagger2.group2.basePackage=com.xxx.group2 # 这里是第二个controller组对应的包名 # groups swagger2.groups=group1,group2然后我们使用全局配置文件,来实现动态Bean的注入,如下:
@Configuration @PropertySources({@PropertySource(value = "classpath:swagger2.properties", ignoreResourceNotFound = true, encoding = "UTF-8")}) @EnableSwagger2 @RestController public class Swagger2UIConfig implements ApplicationContextAware { private ConfigurableApplicationContext configurableApplicationContext; @Autowired private Environment env; @Value("#{'${swagger2.groups}'.split(',')}") private List<String> groups; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.configurableApplicationContext = (ConfigurableApplicationContext) applicationContext; } /** * 按照group的个数,自动生成一系列的Docket bean对象。 * * @return */ @Bean public String createDocket() { groups.forEach(group -> { SwaggerConfigProperties properties = new SwaggerConfigProperties(env, group); BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Docket.class); beanDefinitionBuilder.addConstructorArgValue(DocumentationType.SWAGGER_2); BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition(); BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) configurableApplicationContext.getBeanFactory(); beanFactory.registerBeanDefinition(group, beanDefinition); Docket docket = configurableApplicationContext.getBean(group, Docket.class); docket.groupName(properties.getGroupName()) .enable(properties.getEnable()) .apiInfo(apiInfo(properties.getTitle(), properties.getTitle(), properties.getVersion())) .select() .apis(basePackage(properties.getBasePackage())) .paths(PathSelectors.any()) .build(); }); return "createDocket"; } public static Predicate<RequestHandler> basePackage(final String basePackage) { return input -> Optional.fromNullable(input.declaringClass()).transform( (Function<Class<?>, Boolean>) input1 -> { for (String strPackage : basePackage.split(",")) { boolean isMatch = input1.getPackage().getName().startsWith(strPackage); if (isMatch) { return true; } } return false; } ).or(true); } public ApiInfo apiInfo(String title, String description, String version) { return new ApiInfoBuilder() .title(title) .version(version) .description(description) .termsOfServiceUrl("https://springfox.github.io/springfox/docs/current/") .version(version) .build(); } }做到这一步,你访问http://your ip:port/api/swagger-ui.html的时候就能看到,swaggerUI已经按照你的意思进行了分组,分组工作结束。
SwaggerUI的组织方式为:
包括 包括 包括 包括 包括 包括 包括 包括 group swagger info host basePath tags schemes consumes ...因此我们第一步需要获取到groups:
RestTemplate restTemplate = new RestTemplate(); List groupEntities = restTemplate.getForObject("http://ip:port/contextPath/swagger-resources", ArrayList.class);获取所有的分组之后,我们可以逐个获取分组内部的swagger:
Swagger swagger = getSwagger(groupName, httpServletRequest);为了获取Swagger我们需要注入两个对象,方法如下:
@Autowired private DocumentationCache documentationCache; @Autowired private ServiceModelToSwagger2Mapper mapper; private String hostNameOverride = "DEFAULT"; private String hostName(UriComponents uriComponents) { if ("DEFAULT".equals(this.hostNameOverride)) { String host = uriComponents.getHost(); int port = uriComponents.getPort(); return port > -1 ? String.format("%s:%d", host, port) : host; } else { return this.hostNameOverride; } } private Swagger getSwagger(String groupName, HttpServletRequest request) { Documentation documentation = this.documentationCache.documentationByGroup(groupName); if (documentation != null) { Swagger swagger = this.mapper.mapDocumentation(documentation); UriComponents uriComponents = HostNameProvider.componentsFrom(request, swagger.getBasePath()); swagger.basePath(Strings.isNullOrEmpty(uriComponents.getPath()) ? "/" : uriComponents.getPath()); if (Strings.isNullOrEmpty(swagger.getHost())) { swagger.host(this.hostName(uriComponents)); } return swagger; } return null; }其中用到一个HostNameProvider类:
public class HostNameProvider { public HostNameProvider() { throw new UnsupportedOperationException(); } public static UriComponents componentsFrom(HttpServletRequest request, String basePath) { ServletUriComponentsBuilder builder = fromServletMapping(request, basePath); UriComponents components = UriComponentsBuilder.fromHttpRequest(new ServletServerHttpRequest(request)).build(); String host = components.getHost(); if (!StringUtils.hasText(host)) { return builder.build(); } else { builder.host(host); builder.port(components.getPort()); return builder.build(); } } private static ServletUriComponentsBuilder fromServletMapping(HttpServletRequest request, String basePath) { ServletUriComponentsBuilder builder = ServletUriComponentsBuilder.fromContextPath(request); builder.replacePath(prependForwardedPrefix(request, basePath)); if (StringUtils.hasText((new UrlPathHelper()).getPathWithinServletMapping(request))) { builder.path(request.getServletPath()); } return builder; } private static String prependForwardedPrefix(HttpServletRequest request, String path) { String prefix = request.getHeader("X-Forwarded-Prefix"); return prefix != null ? prefix + path : path; } }这样,Swagger对象已经获取到了。剩下的就可以自己对Swagger对象进行处理。