【稀里糊涂学springmvc】srpingmvc配置文件mvc:resources的处理解析

我们知道,当想要请求一个资源文件,而不是跳到controller中匹配映射时,我们需要在springmvc的xml文件中设置<mvc:resources/>这个标签。

本文将以<mvc:resources mapping="/images/**" location="file:///D:/images/"/>为例,来讲解springmvc对于资源文件的执行过程。

比如通过上面的设置,当我们在页面上通过http://localhost:8088/CipSystem/images/image/20200422/20200422091811_670.png来请求一个图片时,springmvc会到D:/images/下去找对应的图片进行显示。而不会跳转到controller中去进行映射,那么代码的处理过程如何呢,如下:

当请求发送时,实现会被ResourceHttpRequestHandler的handleRequest方法处理,如下:

public class ResourceHttpRequestHandler extends WebContentGenerator
        implements HttpRequestHandler, InitializingBean, CorsConfigurationSource {

  @Override
  public void handleRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

    // For very general mappings (e.g. "/") we need to check 404 first
    Resource resource = getResource(request);
    if (resource == null) {
        logger.trace("No matching resource found - returning 404");
        response.sendError(HttpServletResponse.SC_NOT_FOUND);
        return;
    }

    if (HttpMethod.OPTIONS.matches(request.getMethod())) {
        response.setHeader("Allow", getAllowHeader());
        return;
    }

    // Supported methods and required session
    checkRequest(request);

    // 根据我们图片的请求,来设置一个图片的响应,如:
    if (new ServletWebRequest(
          request,response).checkNotModified(resource.lastModified())) {
        logger.trace("Resource not modified - returning 304");
        return;
    }

    // Apply cache settings, if any
    prepareResponse(response);

    // 根据请求的head,来设置响应的类型,如这里将响应的head设置image/png,
    // 表示将返回一个png图片
    MediaType mediaType = getMediaType(request, resource);
       
    // Content phase
    if (METHOD_HEAD.equals(request.getMethod())) {
        setHeaders(response, resource, mediaType);
        logger.trace("HEAD request - skipping content");
        return;
    }

    ServletServerHttpResponse outputMessage = new 
    ServletServerHttpResponse(response);
    // 将我们的图片作为响应返回到页面中
    if (request.getHeader(HttpHeaders.RANGE) == null) {
        // 设置response中head的内容,比如Content-Length,ContentType
        setHeaders(response, resource, mediaType);
        // 通过流的形式将图片输出到页面上
        this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
    }
    else {
        response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
        ServletServerHttpRequest inputMessage = new 
        ServletServerHttpRequest(request);
        try {
            List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
            response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
            if (httpRanges.size() == 1) {
                ResourceRegion resourceRegion = httpRanges.get(0).
                                                toResourceRegion(resource);
                this.resourceRegionHttpMessageConverter.write(
                                      resourceRegion, mediaType, outputMessage);
            } else {
                this.resourceRegionHttpMessageConverter.write(
                    HttpRange.toResourceRegions(httpRanges, resource), 
                    mediaType, outputMessage);
            } catch (IllegalArgumentException ex) {
              response.setHeader("Content-Range", "bytes */" + 
                                         resource.contentLength());                
              response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
            }
        }
    }

protected Resource getResource(HttpServletRequest request) throws IOException {
    // 从请求中后去请求资源的path	,如image/20200422/20200422091811_670.png	
    String path = (String) request.getAttribute(
                                 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    if (path == null) {
      throw new IllegalStateException(
                    "Required request attribute '" +
					HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE + 
                    "' is not set");
	}
    // 对path进行处理,将连续的/变为一个/
    path = processPath(path);
	if (!StringUtils.hasText(path) || isInvalidPath(path)) {
		return null;
	}
	if (path.contains("%")) {
		try {
			// Use URLDecoder (vs UriUtils) to preserve potentially decoded UTF-8 chars
			if (isInvalidPath(URLDecoder.decode(path, "UTF-8"))) {
				return null;
			}
		}catch (IllegalArgumentException ex) {
				// ignore
			}
		}
		ResourceResolverChain resolveChain = new 
                               DefaultResourceResolverChain(getResourceResolvers());
        // 根据path和xml中配置的location通过PathResourceResolver类来成Resource,代码如下
		Resource resource = resolveChain.resolveResource(request, path, getLocations());
		if (resource == null || getResourceTransformers().isEmpty()) {
			return resource;
		}
		ResourceTransformerChain transformChain =
				new DefaultResourceTransformerChain(
                                resolveChain, getResourceTransformers());
		resource = transformChain.transform(request, resource);
		return resource;
	}
}

Resource是如何生成的呢?在调用Resource resource = resolveChain.resolveResource(request, path, getLocations());时,实际上是最终调用了PathResourceResolver的resolveResourceInternal方法,如下:

public class PathResourceResolver extends AbstractResourceResolver {

  private Resource[] allowedLocations;
    
  @Override
  protected Resource resolveResourceInternal(HttpServletRequest request,
                                             String requestPath,
                                             List<? extends Resource> locations, 
                                             ResourceResolverChain chain) {
	return getResource(requestPath, locations);
  }

/**
*
*/
private Resource getResource(String resourcePath, List<? extends Resource> locations) {
	for (Resource location : locations) {
		try {
			if (logger.isTraceEnabled()) {
				logger.trace("Checking location: " + location);
			}
			Resource resource = getResource(resourcePath, location);
		catch (IOException ex) {}
	}
		return null;
}

/**
* 
*/
protected Resource getResource(String resourcePath, Resource location) throws IOException {        
    // 通过url(我们的location指定的file:/D:/images/)和 
    // resourcePath(image/20200422/20200422091811_670.png)生成一个UrlResource对象
	// Resource resource = location.createRelative(resourcePath);
    // resource.exists():判断是不是file协议,如果是将new一个file,然后通过    
    // AbstractFileResolvingResource的exists和isReadable方法判断这个是否存在是否可读。
	if (resource.exists() && resource.isReadable()) {
         // 判断resource是否合法
      if (checkResource(resource, location)) {
		 return resource;
	  }else if (logger.isTraceEnabled()) {}
		return null;
	}

}

以上便是关于设置<mvc:resource/>后,程序内部执行的核心逻辑。关于springboot内部是否是这样的,暂时未知。

总结:

还是以<mvc:resources mapping="/images/**" location="file:///D:/images/"/>为例,这个location到底可以写什么呢?看下图

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<mvc:resources>是Spring MVC框架中的一个配置标签,用于配置静态资源文件的访问规则。它可以指定静态资源的映射路径和存放位置,并可以通过添加自定义的资源解析器来进一步处理这些静态资源。在引用中的示例中,<mvc:resources>标签的mapping属性指定了将所有请求映射到静态资源的规则,location属性指定了静态资源文件的存放位置,order属性指定了处理顺序。而在引用中的博客中,介绍了如何利用<mvc:resources>标签来访问静态资源文件,并提到了一些注意事项。所以,问题中的"mvc:resources"指的是<mvc:resources>配置标签。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [SpringMVC mvc:resources访问静态资源实战](https://blog.csdn.net/w_linux/article/details/80557822)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [SpringMVC源码研究之 mvc:resources](https://blog.csdn.net/lqzkcx3/article/details/78601545)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值