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 
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!

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