Swagger-ui.html界面打开报404错误

目录

  • Swagger-ui.html界面打开报404错误
    • 初次看到问题时
    • 延伸的知识
    • 总结

Swagger-ui.html界面打开报404错误

遇到的问题是:访问http://localhost:8080/swagger-ui.html没有打开swagger界面,看服务器日志发现没有找到对应的映射:/sagger-ui.html,但是检查了依赖和配置,都是正常的,而且用同样的依赖和配置在另外一个项目环境下是正常的

所以,很好奇为什么会出现这种情况,于是就开始寻找原因

初次看到问题时

  • 猜测一

    • 可能是swagger的配置问题

    • 于是去网上找正确的配置和依赖,这是和springboot集成的版本

      
      <dependency>
          <groupId>io.springfoxgroupId>
          <artifactId>springfox-swagger2artifactId>
          <version>2.7.0version>
      dependency>
      <dependency>
          <groupId>io.springfoxgroupId>
          <artifactId>springfox-swagger-uiartifactId>
          <version>2.7.0version>
      dependency>
      
      @Configuration
      @EnableSwagger2
      public class Swagger2 {
      
          @Bean
          public Docket createRestApi() {
              return new Docket(DocumentationType.SWAGGER_2)
                  .apiInfo(apiInfo())
                  .select()
                  .apis(RequestHandlerSelectors
                  .basePackage("com.onecoderspace.controller"))
                  .paths(PathSelectors.any()).build();
          }
      
          private ApiInfo apiInfo() {
              return new ApiInfoBuilder()
                  .title("spring boot示例接口API")
                  .description("spring boot示例接口API")
                  .version("1.0").build();
          }
      }
      
      • http://start.spring.io上下载一个依赖了web的项目,起名为Demo,然后使用这套配置,访问正常
    • 所以配置没有问题

  • 猜测二

    • 好像没有第二种猜测了,直接查看日志了,其实,第一步做的事情就是查看日志

    • /sagger-ui.html搜索日志发现了No mapping found for HTTP request with URI [/swagger-ui.html] in DispatcherServlet with name 'dispatcherServlet'

    • 得到结论

      • 没有/sagger-ui.html的映射
    • 思考

      • 像在普通的配置了RequestMapping的controller类中,每个方法都有一个路径对应,比如/user/info对应方法getUserInfo(),这种映射的信息会在项目启动的时候打印出来

      • 另外,还有一种映射是资源的映射,比如/sagger-ui.html,也有对应处理机制

      • 为什么这个项目没有映射,Demo项目却可以正常访问

        • 接下来该怎么走

          • 一是:既然有个正常的项目在,就对比这两个项目对/sagger-ui.html请求的处理方式即可发现原因,这也是我的第一个想法,也是这么进行的

            • 于是找到打印日志的代码,搜索到org.springframework.web.servlet.DispatcherServlet#noHandlerFound,发现根据/sagger-ui.html没有获取到对应的HandlerExecutionChain

            • HandlerExecutionChain是通过HandlerMapping接口获得的

              • 从这可以猜测,HandlerMapping是处理映射的接口,那就可能有多种类型的映射(可能是映射的方式不一样),spring对此做了抽象
              • RequestMappingHandlerMapping:请求类型的映射,对应上面举例的/user/info
              • SimpleUrlHandlerMapping:url直接映射到handler
              • 还有很多
            • 通过断点对比发现,现在的项目没有SimpleUrlHandlerMapping这种映射方式,找到了第一个点,继续往下找为什么会没有

            • 自然要看SimpleUrlHandlerMapping是怎么实例化的,然后发现了org.springframework.web.servlet.DispatcherServlet#initHandlerMappings,原来是启动时配置的,但是是在context里获得bean实例,还是不知道在哪里配置的,这条路走不通了

            • 但是回过头来一想,既然要配置,那肯定是在同一个地方操作,这样方便管理,也方便扩展,那么在spring里面哪个配置类是做这个工作的呢

              • 很容易找到WebMvcConfigurationSupport,找到org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping,该方法会通过org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry#getHandlerMapping获得一个SimpleUrlHandlerMapping

              • 但是在getHandlerMapping方法中发现,资源处理器注册中心(ResourceHandlerRegistry)需要注册了资源处理器才会有SimpleUrlHandlerMapping实例化

                protected AbstractHandlerMapping getHandlerMapping() {
                		if (this.registrations.isEmpty()) {
                			return null;
                		}
                		...
                		SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
                		handlerMapping.setOrder(order);
                		handlerMapping.setUrlMap(urlMap);
                		return handlerMapping;
                	}
                
              • 而注册资源处理器的动作是在org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping里面的addResourceHandlers(registry)这个步骤中

              • 到这里可以知道,WebMvcConfigurationSupport是配置资源处理器的地方

              • 回到addResourceHandlers(registry),发现这个方法是给子类实现的,DelegatingWebMvcConfiguration实现了它,相当于把注册的工作交给了DelegatingWebMvcConfiguration,从名称可以看出,它也不干活,只做委托的动作,委托给了它的成员变量WebMvcConfigurerComposite,注意这个composite,说明有很多WebMvcConfigurer

              • 所以,注册的动作最终还是由实现了WebMvcConfigurer接口的实例来实现的,这些实例当中肯定有注册资源处理器的,现在的项目中肯定就是缺少这个实例

              • 查看WebMvcConfigurer的实现,发现了WebMvcAutoConfigurationAdapter,这是springboot里的自动装配,而且它有addResourceHandlers的实现,可能需要它,那接下来就要看这个自动装配为啥没有生效了

              • 来看看生效的条件,因为WebMvcAutoConfigurationAdapter是一个嵌套的配置,所以看它的依附类WebMvcAutoConfiguration的一个条件:@ConditionalOnMissingBean(WebMvcConfigurationSupport.class),可能就是WebMvcConfigurationSupport的实例存在,才导致自动装配失效

              • 那么,检查一下WebMvcConfigurationSupport是怎么实例化的,发现@EnableWebMvc会引入它,那找一下哪里使用了这个注解,接着就找到了项目里面使用它的地方,注释掉,一访问就正常了

          • 二是:去网上搜索“访问swagger报404”的信息

            • 也搜过,开始不知道为什么要这么配,后来发现他们的问题和现在的问题本质是一样的,都是没有注册资源处理器,只不过是自己配置上去,如:

              @Configuration
              public class WebConfig extends WebMvcConfigurationSupport {
              @Override
                  public void addResourceHandlers(ResourceHandlerRegistry registry) {
                      registry.addResourceHandler("/**").addResourceLocations(
                              "classpath:/static/");
                      registry.addResourceHandler("swagger-ui.html").addResourceLocations(
                              "classpath:/META-INF/resources/");
                      registry.addResourceHandler("/webjars/**").addResourceLocations(
                              "classpath:/META-INF/resources/webjars/");
                      super.addResourceHandlers(registry);
                  }
              }
              
            • 在Demo项目中配置,确实有用,但是不放心把这个配置放在现在的项目中,因为还没弄清楚为什么要加上@EnableWebMvc

延伸的知识

  • Filter和Interceptor的区别
  • arraylist和array的区别
    • 这个疑问来源于HandlerExecutionChain中的两个属性:interceptorListinterceptors
  • springboot的两种依赖方式有什么区别,各自的优缺点是什么
  • org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter这个嵌套配置类为什么要配置成嵌套的

总结

  • 猜测对了一个地方,自动装配生效那块,就是因为存在了WebMvcConfigurationSupport的实例,导致WebMvcAutoConfiguration自动装配失效
  • 中间太深入细节了,导致精力耗费太多,最后跳出了细节,从整体把握,得到解决
    • 有些设计模式不熟悉,像适配器、拦截器这种,往往会纠结在这,导致忽略了主线,耗费精力
  • 想想如果没有对比,该怎么进行下去呢
    • 那就得很清楚springmvc的原理了,开始的时候什么也不知道,对注册资源处理器没有什么概念,也只是靠一步步调试、对比,才知道了个大概,具体的细节还不是很清楚,于是就有延伸出来的知识
    • 一次次记录调试的过程,一次次地调整思路

你可能感兴趣的:(spring,源码,springboot,swagger)