一个swagger.enable=false引发启动失败的问题
问题描述
某日部署生产的时候,在application-prod.yml修改配置不启用swagger接口文档,设置swagger.enable=false启动失败了
application-dev.yml
swagger:
enable: true
application-prod.yml
swagger:
enable: false
代码配置类:
package com.config.swagger;
...
@Configuration
@EnableSwagger2
@ConditionalOnExpression("${swagger.enable:false}")
public class SwaggerConfiguration{
...
}
启动报错如下
错误提示:
Parameter 1 of constructor in com.github.xiaoymin.swaggerbootstrapui.web.SwaggerBootstrapUiController required a bean of type 'springfox.documentation.swagger2.mappers.ServiceModelToSwagger2Mapper' that could not be found.
分析过程
测试环境跑的好好的,怎么到了生产就启动失败呢?
其实开始没定位到是swagger这块的问题,这次修改代码没有动swagger这块,也没修改pom文件。开始还以为这个项目比较旧用是spring boot 是1.5.2的但是升级到2.x无果,有些依赖找不到了。然后以为是lib对不上,把生产的lib放到生产也是不行,确认了测试环境和生产环境的pom是一样的。
开启了漫长的排查过程,既然是是swagger有问题,那我就看是swagger的相关的,测试环境是true,那我测试环境也改成false看看?不试不知道,一试下一跳,怎么一个开关就挂了呢?!
这里有好几个教训,且听我娓娓道来。
跟踪报错源码:
还真是依赖ServiceModelToSwagger2Mapper
条件表达式@ConditionalOnExpression("${swagger.enable:false}")
都不让扫描注入swagger相关的,为什么还要扫描?
这个就要spring 是怎么扫描包注入bean的了,不配置的话就是默认的和Application同级的
package com;
...
import java.util.List;
@SpringBootApplication
@EnableScheduling
public class Application extends SpringBootServletInitializer implements InitializingBean {...}
聪明的你会马上发现,Application、SwaggerConfiguration和com.github.xiaoymin.swaggerbootstrapui.web.SwaggerBootstrapUiController
是同属于com包下面的了。所以即使你@ConditionalOnExpression("${swagger.enable:false}")不让
@Configuration、@EnableSwagger2生效,而spring却去扫了!
解决方案
解决方式1:
指定spring扫描的包名
package com;
...
@SpringBootApplication
@EnableScheduling
@ComponentScan(basePackages = {"com.aop","com.common","com.config","com.plugin","com.user","你想让spring扫的包"})
public class Application extends SpringBootServletInitializer implements InitializingBean {...}
这个方式不够灵活,指定太多的包名,下次想新增一个包,不在列表里面就漏了。
解决方式2:
修改Application所属的包结构,如
package com.example;
...
@SpringBootApplication
@EnableScheduling
public class Application extends SpringBootServletInitializer implements InitializingBean {...}```
这个其他包也要跟着改到com.example下面,整个项目工程包名都放在com.example(example公司域名)下面。这样就和依赖包分开了,变动目录比较大,也要同步修改和包名相关的配置,如aop、根据包名反射等。这种方式项目建立之初就应该用这种。
解决方式3:
package com.config.swagger;
...
@Configuration
@EnableSwagger2
public class SwaggerConfiguration extends WebMvcConfigurationSupport {
...
@Value("${swagger.enable:false}")
private boolean swaggerEnable;
@Bean
@Order(value = 1)
public Docket groupRestApi() throws UnknownHostException {
...
return new Docket(DocumentationType.SWAGGER_2).host(InetAddress.getLocalHost().getHostAddress())
.enable(swaggerEnable)//这里控制开关
/* .globalOperationParameters(pars) */.apiInfo(groupApiInfo());
}
}
接口内容是看不到了,但是还是能访问swagger的doc.html页面。只不过空架子。
到此,项目终于顺利起来了!
代码管理规范
可是为什么生产之前配置false也没问题啊!这又是代码git规范问题了。
先反编译看看jar的swaggerConfiguration吧
发现了吗?没有@ConditionalOnExpression("${swagger.enable:false}")
怎么那么奇怪,和本地的代码不一样的!!!
查生产部署文件时间
2020年8月31日,额,上次部署好久的事情的了。
git代码修改时间
@ConditionalOnExpression("${swagger.enable:false}")
新增时间是2021年1月8号,也是挺久, 自己都不记得了!生产代码是哪条commit都不知道了!
这个项目太老了,没有按照git分支规范,自己也懒没创金独立仓库,严格按照分支规范应有master、release、develop、hotfix!每次发版之后dev合并到master。教训啊!!!