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