spring boot 静态资源映射源码

724 阅读2分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

提要

因为项目需要将服务器磁盘中的其他日志展示,所以打算用spring boot 静态资源映射方式将文件展示在页面上,方便下载遇到问题刚好又能学些一波部分源码

代码配置

    ```
package com.hgf.boot.config;

import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * spring mvc的配置
 *   
 */
@Configuration
public class SpringMvcConfiguration implements WebMvcConfigurer {

    /**
     * 静态资源映射
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
       
// 注意这个 addResourceLocations 路劲必须以 / 结尾
registry.addResourceHandler("/work_doc/**").addResourceLocations("file:/Users/hans/work_space/work/work/");
    }

}

源码分析

其实整条链路分为两部分

  • 如何将request符合 /work_doc/**的请求到文件夹/Users/hans/work_space/work/work/映射关系保存
  • 如果访问

映射关系保存

ResourceHandlerRegistration#addResourceLocations

public ResourceHandlerRegistration addResourceLocations(String... resourceLocations) {
   this.locationValues.addAll(Arrays.asList(resourceLocations));
   return this;
}

此时就看this.locationValues 在哪里被调用, ResourceHandlerRegistration#getRequestHandler

protected ResourceHttpRequestHandler getRequestHandler() {
   ResourceHttpRequestHandler handler = new ResourceHttpRequestHandler();
   if (this.resourceChainRegistration != null) {
      handler.setResourceResolvers(this.resourceChainRegistration.getResourceResolvers());
      handler.setResourceTransformers(this.resourceChainRegistration.getResourceTransformers());
   }
   handler.setLocationValues(this.locationValues);
   if (this.cacheControl != null) {
      handler.setCacheControl(this.cacheControl);
   }
   else if (this.cachePeriod != null) {
      handler.setCacheSeconds(this.cachePeriod);
   }
   return handler;
}

一步一步往回走就能看到如下的调用链路

WebMvcConfigurationSupport#resourceHandlerMapping
ResourceHandlerRegistry#getHandlerMapping
ResourceHandlerRegistration#getRequestHandler
// 注册handlerMapping
@Bean
public HandlerMapping resourceHandlerMapping() {
   Assert.state(this.applicationContext != null, "No ApplicationContext set");
   Assert.state(this.servletContext != null, "No ServletContext set");

   ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
         this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
   addResourceHandlers(registry);

   AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
   if (handlerMapping != null) {
      handlerMapping.setPathMatcher(mvcPathMatcher());
      handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
      handlerMapping.setInterceptors(getInterceptors());
      handlerMapping.setCorsConfigurations(getCorsConfigurations());
   }
   else {
      handlerMapping = new EmptyHandlerMapping();
   }
   return handlerMapping;
}

如果访问

spring mvc流程网图如下

image.png

再结合上文中注册到spring容器的BeanHandlerMapping,大概也能猜到流程了 org.springframework.web.servlet.DispatcherServlet#doDispatch(省略版)

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
   .....


   try {
      ModelAndView mv = null;
      Exception dispatchException = null;

      try {

         // 获取处理器映射器
         mappedHandler = getHandler(processedRequest);
         if (mappedHandler == null) {
            noHandlerFound(processedRequest, response);
            return;
         }

         // 获取处理器适配器
         HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

         
         // 调用处理器(处理请求详细部分源码)
         mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
      
         .....
      }
      
      // 渲染返回
      processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
   }
   .......
   
}

详细调用堆栈如图

getResource:186, PathResourceResolver (org.springframework.web.servlet.resource)
getResource:157, PathResourceResolver (org.springframework.web.servlet.resource)
resolveResourceInternal:136, PathResourceResolver (org.springframework.web.servlet.resource)
resolveResource:48, AbstractResourceResolver (org.springframework.web.servlet.resource)
resolveResource:61, DefaultResourceResolverChain (org.springframework.web.servlet.resource)
getResource:529, ResourceHttpRequestHandler (org.springframework.web.servlet.resource)
handleRequest:439, ResourceHttpRequestHandler (org.springframework.web.servlet.resource)
handle:53, HttpRequestHandlerAdapter (org.springframework.web.servlet.mvc)

如果在项目中出现问题按照这个流程一步一步debug就可以找到问题