目录
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
版权声明:本文为博主原创文章,转载请附上博文链接!