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

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

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

初次看到问题时
猜测一

可能是swagger的配置问题

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

<!-- swagger2 -->
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger2</artifactId>
    <version>2.7.0</version>
</dependency>
<dependency>
    <groupId>io.springfox</groupId>
    <artifactId>springfox-swagger-ui</artifactId>
    <version>2.7.0</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
@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();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
去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;
    }
1
2
3
4
5
6
7
8
9
10
而注册资源处理器的动作是在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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
在Demo项目中配置,确实有用,但是不放心把这个配置放在现在的项目中,因为还没弄清楚为什么要加上@EnableWebMvc

延伸的知识
Filter和Interceptor的区别
arraylist和array的区别
这个疑问来源于HandlerExecutionChain中的两个属性:interceptorList、interceptors
springboot的两种依赖方式有什么区别,各自的优缺点是什么
org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter这个嵌套配置类为什么要配置成嵌套的
总结
猜测对了一个地方,自动装配生效那块,就是因为存在了WebMvcConfigurationSupport的实例,导致WebMvcAutoConfiguration自动装配失效
中间太深入细节了,导致精力耗费太多,最后跳出了细节,从整体把握,得到解决
有些设计模式不熟悉,像适配器、拦截器这种,往往会纠结在这,导致忽略了主线,耗费精力
想想如果没有对比,该怎么进行下去呢
那就得很清楚springmvc的原理了,开始的时候什么也不知道,对注册资源处理器没有什么概念,也只是靠一步步调试、对比,才知道了个大概,具体的细节还不是很清楚,于是就有延伸出来的知识
一次次记录调试的过程,一次次地调整思路
--------------------- 
作者:ljm_csdn 
来源:CSDN 
原文:https://blog.csdn.net/ljm_csdn/article/details/87615670 
版权声明:本文为博主原创文章,转载请附上博文链接!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章