一个swagger.enable=false引发启动失败的问题

3,244 阅读1分钟

一个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{
  ...
}

启动报错如下

image.png

错误提示: 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看看?不试不知道,一试下一跳,怎么一个开关就挂了呢?!

这里有好几个教训,且听我娓娓道来。

跟踪报错源码:

image-20210603222134779

还真是依赖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页面。只不过空架子。

image-20210603224238895

到此,项目终于顺利起来了!

代码管理规范

可是为什么生产之前配置false也没问题啊!这又是代码git规范问题了。

先反编译看看jar的swaggerConfiguration吧

image-20210603224827599

发现了吗?没有@ConditionalOnExpression("${swagger.enable:false}")

怎么那么奇怪,和本地的代码不一样的!!!

查生产部署文件时间

image-20210603224619668

2020年8月31日,额,上次部署好久的事情的了。

git代码修改时间

image-20210603225105591

@ConditionalOnExpression("${swagger.enable:false}")

新增时间是2021年1月8号,也是挺久, 自己都不记得了!生产代码是哪条commit都不知道了!

这个项目太老了,没有按照git分支规范,自己也懒没创金独立仓库,严格按照分支规范应有master、release、develop、hotfix!每次发版之后dev合并到master。教训啊!!!