Spring Boot:2.2.6
@Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
    @Value("${external.resource.path.type.a}")
    private String externalResourcePathTypeA;
    @Value("${external.resource.path.type.b}")
    private String externalResourcePathTypeB;
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/typea/**").addResourceLocations(externalResourcePathTypeA);
        registry.addResourceHandler("/typeb/**").addResourceLocations(externalResourcePathTypeB);
    }
}
external.resource.path.type.a=file:E:/test/typea
external.resource.path.type.b=file:E:/test/typeb
E:\test
├─typea
│  └─1
│          1.png
│
└─typeb
    └─04e417e185c64f00b58ef4b1f18defeb
            2.png

external.resource.path.type.a=file:E:/test/typea/
external.resource.path.type.b=file:E:/test/typeb/
[nio-8080-exec-3] o.s.w.s.DispatcherServlet                : GET "/typea/1/1.png", parameters={}, headers={masked} in DispatcherServlet ‘dispatcherServlet‘
[nio-8080-exec-3] o.s.w.s.h.SimpleUrlHandlerMapping        : Matching patterns [/typea/**, /**]
[nio-8080-exec-3] o.s.w.s.h.SimpleUrlHandlerMapping        : Mapped to HandlerExecutionChain with [ResourceHttpRequestHandler ["file:E:/test/typea"]] and 3 interceptors
[nio-8080-exec-3] o.s.w.s.r.ResourceHttpRequestHandler     : Resource not found
[nio-8080-exec-3] o.s.w.s.DispatcherServlet                : No view rendering, null ModelAndView returned.

@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.debug("Resource not found");
      response.sendError(HttpServletResponse.SC_NOT_FOUND);
      return;
   }
   //...
}
@Nullable
protected Resource getResource(HttpServletRequest request) throws IOException {
   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 = processPath(path);
   if (!StringUtils.hasText(path) || isInvalidPath(path)) {
      return null;
   }
   if (isInvalidEncodedPath(path)) {
      return null;
   }
   Assert.notNull(this.resolverChain, "ResourceResolverChain not initialized.");
   Assert.notNull(this.transformerChain, "ResourceTransformerChain not initialized.");
   Resource resource = this.resolverChain.resolveResource(request, path, getLocations());
   if (resource != null) {
      resource = this.transformerChain.transform(request, resource);
   }
   return resource;
}
@Nullable
private Resource getResource(String resourcePath, @Nullable HttpServletRequest request,
                             List<? extends Resource> locations) {
    for (Resource location : locations) {
        try {
            String pathToUse = encodeIfNecessary(resourcePath, request, location);
            Resource resource = getResource(pathToUse, location);
            if (resource != null) {
                return resource;
            }
        }
        catch (IOException ex) {
            if (logger.isDebugEnabled()) {
                String error = "Skip location [" + location + "] due to error";
                if (logger.isTraceEnabled()) {
                    logger.trace(error, ex);
                }
                else {
                    logger.debug(error + ": " + ex.getMessage());
                }
            }
        }
    }
    return null;
}
@Nullable
protected Resource getResource(String resourcePath, Resource location) throws IOException {
   Resource resource = location.createRelative(resourcePath);
   if (resource.isReadable()) {
      if (checkResource(resource, location)) {
         return resource;
      }
      else if (logger.isWarnEnabled()) {
         Resource[] allowedLocations = getAllowedLocations();
         logger.warn("Resource path \"" + resourcePath + "\" was successfully resolved " +
               "but resource \"" +    resource.getURL() + "\" is neither under the " +
               "current location \"" + location.getURL() + "\" nor under any of the " +
               "allowed locations " + (allowedLocations != null ? Arrays.asList(allowedLocations) : "[]"));
      }
   }
   return null;
}
protected URL createRelativeURL(String relativePath) throws MalformedURLException {
   if (relativePath.startsWith("/")) {
      relativePath = relativePath.substring(1);
   }
   // # can appear in filenames, java.net.URL should not treat it as a fragment
   relativePath = StringUtils.replace(relativePath, "#", "%23");
   // Use the URL constructor for applying the relative path as a URL spec
   return new URL(this.url, relativePath);
}


protected void parseURL(URL u, String spec, int start, int limit) {
	//...
    // Parse the file path if any
    if (start < limit) {
        if (spec.charAt(start) == ‘/‘) {
            path = spec.substring(start, limit);
        } else if (path != null && path.length() > 0) {
            isRelPath = true;
            int ind = path.lastIndexOf(‘/‘);
            String seperator = "";
            if (ind == -1 && authority != null)
                seperator = "/";
            path = path.substring(0, ind + 1) + seperator +
                spec.substring(start, limit);
        } else {
            String seperator = (authority != null) ? "/" : "";
            path = seperator + spec.substring(start, limit);
        }
    }
    //...
}

a) All but the last segment of the base URI‘s path component is
   copied to the buffer.  In other words, any characters after the
   last (right-most) slash character, if any, are excluded.
Spring Boot--带着问题看源码2--实现WebMvcConfigurer接口addResourceHandlers方法,无法访问外部资源
原文:https://www.cnblogs.com/wxyzyu/p/12664043.html