SpringBoot访问静态资源报404——记录一次调试过程与解决方案

SpringBootWeb项目中,默认的静态资源路径有下列4种:

  1. classpath:/META-INF/resources
  2. classpath:/resources
  3. classpath:/static
  4. classpath:/public**
    SpringBoot访问静态资源报404——记录一次调试过程与解决方案_第1张图片

默认情况下,浏览器中输入localhost:port/index.html将会成功访问到该资源文件[静态资源路径符合第三点],但是却出乎意料,报了404找不到该资源。
SpringBoot访问静态资源报404——记录一次调试过程与解决方案_第2张图片
在调试之前需要回顾一下SpringMVC中DispatchServelet的工作流程,如何处理一个请求并将结果成功返回给浏览器
DispatchServelet工作流程

不得不提的是,DispatchServelet其中一个最重要的一个步骤就是从HandlerMapping中获得处理请求的Handler
SpringBoot访问静态资源报404——记录一次调试过程与解决方案_第3张图片
调试DispatchServelet时,可得知获得该Handler为空,所以才会进入noHandlerFound()方法,对请求进一步处理

	protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (pageNotFoundLogger.isWarnEnabled()) {
			pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
		}
		if (this.throwExceptionIfNoHandlerFound) {
			throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
					new ServletServerHttpRequest(request).getHeaders());
		}
		else {
		    //Http请求返回404
			response.sendError(HttpServletResponse.SC_NOT_FOUND);
		}
	}

所以这里返回404的原因就是对输入的请求无法找到一个合适的处理器,获得handler的源码如下:

	protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		if (this.handlerMappings != null) {
			for (HandlerMapping mapping : this.handlerMappings) {
				HandlerExecutionChain handler = mapping.getHandler(request);
				if (handler != null) {
					return handler;
				}
			}
		}
		return null;
	}

SpringBoot访问静态资源报404——记录一次调试过程与解决方案_第4张图片
无法从注册的HangdlerMappings列表中通过一定的映射关系获得HandlerExecutionChain【handler和拦截器的结合】

SimpleUrlHandlerMapping : 它内部有一个Map urlMap,存放着各个url对应的handler,适用性最强的Handler Mapping类,允许明确指定URL模式和Handler的映射关系。处理静态资源的就是从这个SimpleUrlHandlerMapping 中获得Handler

继续调试可知

SpringBoot访问静态资源报404——记录一次调试过程与解决方案_第5张图片

SimpleUrlHandlerMapping 的UrlMap存放的映射无法支持找到static/index.html,允许访问的资源路径仅仅只有classpath:/META-INF/resources/ 和classpath:/META-INF/resources/webjars/
根本原因找到了,我曾经重写了资源路径的方法,所以才无法访问到static文件夹下的index.html

public class SwaggerConfig implements WebMvcConfigurer {
    //忽略其他业务代码

    //重写addResourceHandlers 静态资源文件映射配置
    //当每次调用registry.addResourceHandler()时,实际上它会创建一个ResourceHandlerRegistration,
    //然后将该对象放到自己的注册表管理起来,函数参数pathPatterns 表示要映射到URL pattern, 可以传递
    //多个要映射到的URL pattern。
    // pathPatterns 表示要映射到URL pattern, 可以传递多个要映射到的URLpattern,
    //addResourceLocations(...)中的可变参数则代表着对应的静态资源路径
    
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

因此解决办法就是,添加默认的资源文件访问路径

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {

        //添加默认的静态资源访问路径
       registry.addResourceHandler("/**")
               .addResourceLocations("classpath:static/","classpath:META-IFA/resources/","classpath:resources/","classpath:public/","classpath:/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");

    }

新增上述代码后,访问成功!

后记:上述调试大概花费了一个多小时,才知道原来访问默认的静态文件夹下的文件生效的原因。顺便也加深了DispatchServelet 的工作流程,看样子还是得知其然才行

你可能感兴趣的:(java学习笔记,mvc)