1. <servlet>
  2. <servlet-name>dispatcher</servlet-name>
  3. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  4. <async-supported>false</async-supported>
  5. </servlet>
  6. <servlet-mapping>
  7. <servlet-name>dispatcher</servlet-name>
  8. <url-pattern>/</url-pattern>
  9. </servlet-mapping>

在javaweb项目中配置了DispatcherServlet的情况下,如果不进行额外配置的话,几乎所有的请求都会走这个servlet来处理,默认静态资源按路径是访问不到的会报404错误,下面讲一讲如何配置才能访问到静态资源,本文将介绍三种方法

1. 在java配置文件中配置DefaultServletHttpRequestHandler来进行处理

  1. @Configuration
  2. @EnableWebMvc
  3. public class MyMvcConfigurer implements WebMvcConfigurer {
  4. @Override
  5. public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
  6. // tomcat默认处理静态资源的servlet名称为default,不指定也可以DefaultServletHttpRequestHandler.setServletContext会自动获取
  7. // configurer.enable("default");
  8. configurer.enable();
  9. }
  10. }

上述配置完成后org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#defaultServletHandlerMapping 方法会生成一个类名为SimpleUrlHandlerMapping的bean,当其他handlerMapping无法处理请求时会接着调用SimpleUrlHandlerMapping对象进行处理

  1. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#defaultServletHandlerMapping
  2. /**
  3. * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
  4. * default servlet handler. To configure "default" Servlet handling,
  5. * override {@link #configureDefaultServletHandling}.
  6. */
  7. @Bean
  8. public HandlerMapping defaultServletHandlerMapping() {
  9. Assert.state(this.servletContext != null, "No ServletContext set");
  10. DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
  11. configureDefaultServletHandling(configurer);
  12. HandlerMapping handlerMapping = configurer.buildHandlerMapping();
  13. return (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping());
  14. }
  15. org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer#buildHandlerMapping
  16. @Nullable
  17. protected SimpleUrlHandlerMapping buildHandlerMapping() {
  18. if (this.handler == null) {
  19. return null;
  20. }
  21. SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
  22. handlerMapping.setUrlMap(Collections.singletonMap("/**", this.handler));
  23. handlerMapping.setOrder(Integer.MAX_VALUE);
  24. return handlerMapping;
  25. }

SimpleUrlHandlerMapping中有一个urlMap属性,key为请求路径匹配模式串,'/**'能匹配所有的路径, value为handler匹配完成后会调用handler处理请求

下面这个方法主要用来匹配获取handler

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
  1.   @Override
  2. @Nullable
  3. protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
  4. // 请求静态资源 path=/zxq/static/login.png
  5. // 处理完lookupPath=/static/login.png
  6. String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
  7. Object handler = lookupHandler(lookupPath, request);
  8. if (handler == null) {
  9. // We need to care for the default handler directly, since we need to
  10. // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
  11. Object rawHandler = null;
  12. if ("/".equals(lookupPath)) {
  13. rawHandler = getRootHandler();
  14. }
  15. if (rawHandler == null) {
  16. rawHandler = getDefaultHandler();
  17. }
  18. if (rawHandler != null) {
  19. // Bean name or resolved handler?
  20. if (rawHandler instanceof String) {
  21. String handlerName = (String) rawHandler;
  22. rawHandler = obtainApplicationContext().getBean(handlerName);
  23. }
  24. validateHandler(rawHandler, request);
  25. handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
  26. }
  27. }
  28. if (handler != null && logger.isDebugEnabled()) {
  29. logger.debug("Mapping [" + lookupPath + "] to " + handler);
  30. }
  31. else if (handler == null && logger.isTraceEnabled()) {
  32. logger.trace("No handler mapping found for [" + lookupPath + "]");
  33. }
  34. return handler;
  35. }
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler
  1.   /**
  2. * Look up a handler instance for the given URL path.
  3. * <p>Supports direct matches, e.g. a registered "/test" matches "/test",
  4. * and various Ant-style pattern matches, e.g. a registered "/t*" matches
  5. * both "/test" and "/team". For details, see the AntPathMatcher class.
  6. * <p>Looks for the most exact pattern, where most exact is defined as
  7. * the longest path pattern.
  8. * @param urlPath the URL the bean is mapped to
  9. * @param request current HTTP request (to expose the path within the mapping to)
  10. * @return the associated handler instance, or {@code null} if not found
  11. * @see #exposePathWithinMapping
  12. * @see org.springframework.util.AntPathMatcher
  13. */
  14. @Nullable
  15. protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
  16. // Direct match?
  17. // 精确匹配,是否有符合的handler
  18. // urlPath = /static/login.png
  19. Object handler = this.handlerMap.get(urlPath);
  20. if (handler != null) {
  21. // Bean name or resolved handler?
  22. if (handler instanceof String) {
  23. String handlerName = (String) handler;
  24. handler = obtainApplicationContext().getBean(handlerName);
  25. }
  26. validateHandler(handler, request);
  27. return buildPathExposingHandler(handler, urlPath, urlPath, null);
  28. }
  29. // Pattern match?
  30. // 路径匹配
  31. List<String> matchingPatterns = new ArrayList<>();
  32. for (String registeredPattern : this.handlerMap.keySet()) {
  33. if (getPathMatcher().match(registeredPattern, urlPath)) {
  34. matchingPatterns.add(registeredPattern);
  35. }
  36. else if (useTrailingSlashMatch()) {
  37. if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
  38. matchingPatterns.add(registeredPattern + "/");
  39. }
  40. }
  41. }
  42. String bestMatch = null;
  43. Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
  44. if (!matchingPatterns.isEmpty()) {
  45. matchingPatterns.sort(patternComparator);
  46. if (logger.isDebugEnabled()) {
  47. logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
  48. }
  49. bestMatch = matchingPatterns.get(0);
  50. }
  51. // bestMatch = /static/**
  52. if (bestMatch != null) {
  53. handler = this.handlerMap.get(bestMatch);
  54. if (handler == null) {
  55. if (bestMatch.endsWith("/")) {
  56. handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
  57. }
  58. if (handler == null) {
  59. throw new IllegalStateException(
  60. "Could not find handler for best pattern match [" + bestMatch + "]");
  61. }
  62. }
  63. // Bean name or resolved handler?
  64. if (handler instanceof String) {
  65. String handlerName = (String) handler;
  66. handler = obtainApplicationContext().getBean(handlerName);
  67. }
  68. validateHandler(handler, request);
  69. // login.png
  70. String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);
  71. // There might be multiple 'best patterns', let's make sure we have the correct URI template variables
  72. // for all of them
  73. Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
  74. for (String matchingPattern : matchingPatterns) {
  75. if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
  76. Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
  77. Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
  78. uriTemplateVariables.putAll(decodedVars);
  79. }
  80. }
  81. if (logger.isDebugEnabled()) {
  82. logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
  83. }
  84. // /static/** login.png
  85. return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
  86. }
  87. // No handler found...
  88. return null;
  89. }

接着调用DefaultServletHttpRequestHandler的handleRequest方法处理请求,逻辑比较简单,获取请求转发器进行请求转发交给tomcat默认的servlet来进行处理

  1. @Override
  2. public void handleRequest(HttpServletRequest request, HttpServletResponse response)
  3. throws ServletException, IOException {
  4. Assert.state(this.servletContext != null, "No ServletContext set");
  5. RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
  6. if (rd == null) {
  7. throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
  8. this.defaultServletName + "'");
  9. }
  10. rd.forward(request, response);
  11. }

2. 在java配置文件中配置ResourceHttpRequestHandler来进行处理

  1. @Configuration
  2. @EnableWebMvc
  3. public class MyMvcConfigurer implements WebMvcConfigurer {
  4. @Override
  5. public void addResourceHandlers(ResourceHandlerRegistry registry) {
  6. registry.addResourceHandler("/static/**").addResourceLocations("/static/");
  7. }
  8. }

和第一种配置几乎一样,其实只是换了一个handler类型来处理请求罢了

上述配置完成后org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping 方法会生成一个类名为SimpleUrlHandlerMapping的bean,当其他handlerMapping无法处理请求时会接着调用SimpleUrlHandlerMapping对象进行处理

ResourceHttpRequestHandler比DefaultServletHttpRequestHandler的构建稍微复杂一点

  1. org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping
  2. /**
  3. * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
  4. * resource handlers. To configure resource handling, override
  5. * {@link #addResourceHandlers}.
  6. */
  7. @Bean
  8. public HandlerMapping resourceHandlerMapping() {
  9. Assert.state(this.applicationContext != null, "No ApplicationContext set");
  10. Assert.state(this.servletContext != null, "No ServletContext set");
  11. ResourceHandlerRegistry registry = new ResourceHandlerRegistry(this.applicationContext,
  12. this.servletContext, mvcContentNegotiationManager(), mvcUrlPathHelper());
  13. addResourceHandlers(registry);
  14. AbstractHandlerMapping handlerMapping = registry.getHandlerMapping();
  15. if (handlerMapping != null) {
  16. handlerMapping.setPathMatcher(mvcPathMatcher());
  17. handlerMapping.setUrlPathHelper(mvcUrlPathHelper());
  18. handlerMapping.setInterceptors(getInterceptors());
  19. handlerMapping.setCorsConfigurations(getCorsConfigurations());
  20. }
  21. else {
  22. handlerMapping = new EmptyHandlerMapping();
  23. }
  24. return handlerMapping;
  25. }
  26. org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry#getHandlerMapping
  27. /**
  28. * Return a handler mapping with the mapped resource handlers; or {@code null} in case
  29. * of no registrations.
  30. */
  31. @Nullable
  32. protected AbstractHandlerMapping getHandlerMapping() {
  33. if (this.registrations.isEmpty()) {
  34. return null;
  35. }
  36. Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();
  37. for (ResourceHandlerRegistration registration : this.registrations) {
  38. for (String pathPattern : registration.getPathPatterns()) {
  39. ResourceHttpRequestHandler handler = registration.getRequestHandler();
  40. if (this.pathHelper != null) {
  41. handler.setUrlPathHelper(this.pathHelper);
  42. }
  43. if (this.contentNegotiationManager != null) {
  44. handler.setContentNegotiationManager(this.contentNegotiationManager);
  45. }
  46. handler.setServletContext(this.servletContext);
  47. handler.setApplicationContext(this.applicationContext);
  48. try {
  49. handler.afterPropertiesSet();
  50. }
  51. catch (Throwable ex) {
  52. throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
  53. }
  54. urlMap.put(pathPattern, handler);
  55. }
  56. }
  57. SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
  58. handlerMapping.setOrder(order);
  59. handlerMapping.setUrlMap(urlMap);
  60. return handlerMapping;
  61. }

之后也是调用SimpleUrlHandlerMapping相同的逻辑先根据请求路径匹配找到对应处理的handler,这里对应的是ResourceHttpRequestHandler之后调用handleRequest方法,原理是先根据请求的路径找到对应的资源文件,再获取资源文件的输入流写入到response响应中,源码如下:

org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest
  1.  /**
  2. * Processes a resource request.
  3. * <p>Checks for the existence of the requested resource in the configured list of locations.
  4. * If the resource does not exist, a {@code 404} response will be returned to the client.
  5. * If the resource exists, the request will be checked for the presence of the
  6. * {@code Last-Modified} header, and its value will be compared against the last-modified
  7. * timestamp of the given resource, returning a {@code 304} status code if the
  8. * {@code Last-Modified} value is greater. If the resource is newer than the
  9. * {@code Last-Modified} value, or the header is not present, the content resource
  10. * of the resource will be written to the response with caching headers
  11. * set to expire one year in the future.
  12. */
  13. @Override
  14. public void handleRequest(HttpServletRequest request, HttpServletResponse response)
  15. throws ServletException, IOException {
  16. // For very general mappings (e.g. "/") we need to check 404 first
  17. // 根据请求的文件路径找到对应的资源文件
  18. Resource resource = getResource(request);
  19. if (resource == null) {
  20. logger.trace("No matching resource found - returning 404");
  21. response.sendError(HttpServletResponse.SC_NOT_FOUND);
  22. return;
  23. }
  24. if (HttpMethod.OPTIONS.matches(request.getMethod())) {
  25. response.setHeader("Allow", getAllowHeader());
  26. return;
  27. }
  28. // Supported methods and required session
  29. // 校验支持的方法GET和HEAD 以及验证session是否必须
  30. checkRequest(request);
  31. // Header phase
  32. if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
  33. logger.trace("Resource not modified - returning 304");
  34. return;
  35. }
  36. // Apply cache settings, if any
  37. // 可以根据设置的秒数设置缓存时间 cache-control:max-age=xxx
  38. prepareResponse(response);
  39. // Check the media type for the resource
  40. // 根据文件后缀去寻找 png -> image/png
  41. MediaType mediaType = getMediaType(request, resource);
  42. if (mediaType != null) {
  43. if (logger.isTraceEnabled()) {
  44. logger.trace("Determined media type '" + mediaType + "' for " + resource);
  45. }
  46. }
  47. else {
  48. if (logger.isTraceEnabled()) {
  49. logger.trace("No media type found for " + resource + " - not sending a content-type header");
  50. }
  51. }
  52. // Content phase
  53. if (METHOD_HEAD.equals(request.getMethod())) {
  54. setHeaders(response, resource, mediaType);
  55. logger.trace("HEAD request - skipping content");
  56. return;
  57. }
  58. ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
  59. if (request.getHeader(HttpHeaders.RANGE) == null) {
  60. Assert.state(this.resourceHttpMessageConverter != null, "Not initialized");
  61. // 设置content-type、content-length等响应头
  62. setHeaders(response, resource, mediaType);
  63. // 将文件流写入到response响应中
  64. this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
  65. }
  66. else {
  67. Assert.state(this.resourceRegionHttpMessageConverter != null, "Not initialized");
  68. response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
  69. ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
  70. try {
  71. List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
  72. response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
  73. this.resourceRegionHttpMessageConverter.write(
  74. HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage);
  75. }
  76. catch (IllegalArgumentException ex) {
  77. response.setHeader("Content-Range", "bytes */" + resource.contentLength());
  78. response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
  79. }
  80. }
  81. }

3.  web.xml配置servlet映射

原理可以参考上一篇文章

  1. <servlet-mapping>
  2. <servlet-name>default</servlet-name>
  3. <url-pattern>/static/*</url-pattern>
  4. </servlet-mapping>

将带有/static/xxx 路径的请求直接交给tomcat默认的servlet去进行处理

最后

完成上述的一种配置后就能访问到我们的静态资源了,请求路径http://localhost:8082/zxq/static/login.png

springmvc静态资源配置的更多相关文章

  1. Springboot系列(四)web静态资源配置详解

    Springboot系列(四)web静态资源配置 往期精彩 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 SpringBoot系列(三)配 ...

  2. nginx代理配置 配置中的静态资源配置,root 和 alias的区别。启动注意事项

    这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...

  3. nginx代理配置 配置中的静态资源配置,root 和 alias的区别

    这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...

  4. spring boot mvc系列-静态资源配置与MappingHandler拦截器

    静态资源配置 Spring Boot 默认将 /** 所有访问映射到以下目录: classpath:/static classpath:/public classpath:/resources cla ...

  5. springMVC 静态资源加版本号

    springMVC 静态资源加版本号 http://blog.csdn.net/zhangt85/article/details/42126275

  6. Springboot项目静态资源配置

    springboot项目的静态资源配置网上有好多,说的也很详细 我今天出错是自定义了一个filter,在shiro里配置的/**,自定义filter 所以一直报302

  7. springmvc静态资源;mvc:default-servlet-handler后Controller失效

    springmvc静态资源;mvc:default-servlet-handler后Controller失效 web.xml配置<url-pattern>/</url-pattern ...

  8. springboot(八)内置SpringMvc静态文件地址修改

    参考:作者:恒宇少年链接:https://www.jianshu.com/p/c6ab1081fd5f   介绍: SpringMVC大家都不陌生,而被SpringBoot集成的SpringMVC除了 ...

  9. Django_静态资源配置和ajax(九)

    一.静态资源配置 静态资源的相关配置都在项目目录下的 settings.py 文件中进行配置.配置参数如下: # 浏览器访问静态资源时的路径 STATIC_URL = '/static2/' # 存放 ...

随机推荐

  1. 封装axios请求

    import axios from 'axios' import router from '@/router' axios.defaults.baseURL = system.requestBaseU ...

  2. Docker打包镜像并上传

    Docker打包镜像并上传 登录 账号 docker login --username=yourusername 密码 yourPassword 推送到仓库 docker镜像打标签 docker ta ...

  3. node.js 创建 wss服务

    var https=require('https'); var ws=require('ws'); var fs=require('fs'); var keypath=process.cwd()+'/ ...

  4. JVM学习笔记-从底层了解程序运行(二)

    解决JVM运行中的问题 一个案例理解常用工具 测试代码: /** * 从数据库中读取信用数据,套用模型,并把结果进行记录和传输 */ public class T15_FullGC_Problem01 ...

  5. Kubernetes-23:详解如何将CPU Manager做到游刃有余

    k8s中为什么要用CPU Manager? 默认情况下,kubelet 使用CFS配额来执行 Pod 的 CPU 约束.Kubernetes的Node节点会运行多个Pod,其中会有部分的Pod属于CP ...

  6. vi与vim使用

    简介 Vi是一个命令行界面下的文本编辑工具(最早1976年由Bill Joy开发,原名ex),vi 支持就大多数操作系统(最早在BSD上发布)并且功能已经十分强大. 1991年Bram Moolena ...

  7. 浅议.NET遗留应用改造

    浅议.NET遗留应用改造 TLDR:本文介绍了遗留应用改造中的一些常见问题,并对改造所能开展的目标.原则.策略进行了概述. 一.背景概述 1.概述 或许仅"遗留应用"这个标题就比较 ...

  8. 利用MATLAB仿真最小发射功率下WSN的连通性和覆盖率

    一.目的 (1)在固定节点个数的前提下,仿真求得使网络保持连通的最小通信半径(最低能级). (2)在上述节点个数和通信半径的前提下,计算随机布撒的节点的覆盖率. 二.方法描述 (1)首先假设通信半径都 ...

  9. 循环控制-break语句和continue语句

    break关键字的用法有常见的两种: 1.可以用switch语句当中,一旦执行,整个switch语句立刻结束 2.还可以用在循环语句当中,一定执行,整个循环语句立刻结束,打断循环 关于循环的选择,有一 ...

  10. Eolink 全局搜索介绍【翻译】

    随着前后端分离成为互联网项目开发的标准模式, API 成为了前后端联通的桥梁.而面对越来越频繁和复杂的调用需求,项目里的 API 数量也越来越多,我们需要通过搜索功能来快速定位到对应的 API来进行使 ...