對於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到底可以寫什麼呢?看下圖

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章