欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringMVC靜態(tài)資源配置過程詳解

 更新時間:2022年08月12日 09:02:54   作者:默念x  
在javaweb項目中配置了DispatcherServlet的情況下,如果不進(jìn)行額外配置的話,幾乎所有的請求都會走這個servlet來處理,默認(rèn)靜態(tài)資源按路徑是訪問不到的會報404錯誤,下面就來講一講如何配置才能訪問到靜態(tài)資源吧

springmvc靜態(tài)資源配置

  <servlet>
    <servlet-name>dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <async-supported>false</async-supported>
  </servlet>

  <servlet-mapping>
    <servlet-name>dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>

在javaweb項目中配置了DispatcherServlet的情況下,如果不進(jìn)行額外配置的話,幾乎所有的請求都會走這個servlet來處理,默認(rèn)靜態(tài)資源按路徑是訪問不到的會報404錯誤,下面講一講如何配置才能訪問到靜態(tài)資源,本文將介紹三種方法

1. 在java配置文件中配置DefaultServletHttpRequestHandler來進(jìn)行處理 

@Configuration
@EnableWebMvc
public class MyMvcConfigurer implements WebMvcConfigurer {

  @Override
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    // tomcat默認(rèn)處理靜態(tài)資源的servlet名稱為default,不指定也可以DefaultServletHttpRequestHandler.setServletContext會自動獲取
//    configurer.enable("default");
    configurer.enable();
  }
}

上述配置完成后org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#defaultServletHandlerMapping 方法會生成一個類名為SimpleUrlHandlerMapping的bean,當(dāng)其他handlerMapping無法處理請求時會接著調(diào)用SimpleUrlHandlerMapping對象進(jìn)行處理

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#defaultServletHandlerMapping
/**
 * Return a handler mapping ordered at Integer.MAX_VALUE with a mapped
 * default servlet handler. To configure "default" Servlet handling,
 * override {@link #configureDefaultServletHandling}.
 */
@Bean
public HandlerMapping defaultServletHandlerMapping() {
  Assert.state(this.servletContext != null, "No ServletContext set");
  DefaultServletHandlerConfigurer configurer = new DefaultServletHandlerConfigurer(this.servletContext);
  configureDefaultServletHandling(configurer);

  HandlerMapping handlerMapping = configurer.buildHandlerMapping();
  return (handlerMapping != null ? handlerMapping : new EmptyHandlerMapping());
}


org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer#buildHandlerMapping
@Nullable
protected SimpleUrlHandlerMapping buildHandlerMapping() {
  if (this.handler == null) {
    return null;
  }

  SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
  handlerMapping.setUrlMap(Collections.singletonMap("/**", this.handler));
  handlerMapping.setOrder(Integer.MAX_VALUE);
  return handlerMapping;
}

SimpleUrlHandlerMapping中有一個urlMap屬性,key為請求路徑匹配模式串,'/**'能匹配所有的路徑, value為handler匹配完成后會調(diào)用handler處理請求 

下面這個方法主要用來匹配獲取handler

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal

?	@Override
	@Nullable
	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
		// 請求靜態(tài)資源 path=/zxq/static/login.png
		// 處理完lookupPath=/static/login.png
		String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			// We need to care for the default handler directly, since we need to
			// expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well.
			Object rawHandler = null;
			if ("/".equals(lookupPath)) {
				rawHandler = getRootHandler();
			}
			if (rawHandler == null) {
				rawHandler = getDefaultHandler();
			}
			if (rawHandler != null) {
				// Bean name or resolved handler?
				if (rawHandler instanceof String) {
					String handlerName = (String) rawHandler;
					rawHandler = obtainApplicationContext().getBean(handlerName);
				}
				validateHandler(rawHandler, request);
				handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
			}
		}
		if (handler != null && logger.isDebugEnabled()) {
			logger.debug("Mapping [" + lookupPath + "] to " + handler);
		}
		else if (handler == null && logger.isTraceEnabled()) {
			logger.trace("No handler mapping found for [" + lookupPath + "]");
		}
		return handler;
	}

org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#lookupHandler

?	/**
	 * Look up a handler instance for the given URL path.
	 * <p>Supports direct matches, e.g. a registered "/test" matches "/test",
	 * and various Ant-style pattern matches, e.g. a registered "/t*" matches
	 * both "/test" and "/team". For details, see the AntPathMatcher class.
	 * <p>Looks for the most exact pattern, where most exact is defined as
	 * the longest path pattern.
	 * @param urlPath the URL the bean is mapped to
	 * @param request current HTTP request (to expose the path within the mapping to)
	 * @return the associated handler instance, or {@code null} if not found
	 * @see #exposePathWithinMapping
	 * @see org.springframework.util.AntPathMatcher
	 */
	@Nullable
	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// Direct match?
		// 精確匹配,是否有符合的handler
		// urlPath = /static/login.png
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			// Bean name or resolved handler?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			return buildPathExposingHandler(handler, urlPath, urlPath, null);
		}

		// Pattern match?
		// 路徑匹配
		List<String> matchingPatterns = new ArrayList<>();
		for (String registeredPattern : this.handlerMap.keySet()) {
			if (getPathMatcher().match(registeredPattern, urlPath)) {
				matchingPatterns.add(registeredPattern);
			}
			else if (useTrailingSlashMatch()) {
				if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
					matchingPatterns.add(registeredPattern + "/");
				}
			}
		}

		String bestMatch = null;
		Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
		if (!matchingPatterns.isEmpty()) {
			matchingPatterns.sort(patternComparator);
			if (logger.isDebugEnabled()) {
				logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
			}
			bestMatch = matchingPatterns.get(0);
		}
		// bestMatch = /static/**
		if (bestMatch != null) {
			handler = this.handlerMap.get(bestMatch);
			if (handler == null) {
				if (bestMatch.endsWith("/")) {
					handler = this.handlerMap.get(bestMatch.substring(0, bestMatch.length() - 1));
				}
				if (handler == null) {
					throw new IllegalStateException(
							"Could not find handler for best pattern match [" + bestMatch + "]");
				}
			}
			// Bean name or resolved handler?
			if (handler instanceof String) {
				String handlerName = (String) handler;
				handler = obtainApplicationContext().getBean(handlerName);
			}
			validateHandler(handler, request);
			// login.png
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestMatch, urlPath);

			// There might be multiple 'best patterns', let's make sure we have the correct URI template variables
			// for all of them
			Map<String, String> uriTemplateVariables = new LinkedHashMap<>();
			for (String matchingPattern : matchingPatterns) {
				if (patternComparator.compare(bestMatch, matchingPattern) == 0) {
					Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
					Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
					uriTemplateVariables.putAll(decodedVars);
				}
			}
			if (logger.isDebugEnabled()) {
				logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
			}
			// /static/**   login.png
			return buildPathExposingHandler(handler, bestMatch, pathWithinMapping, uriTemplateVariables);
		}

		// No handler found...
		return null;
	}

接著調(diào)用DefaultServletHttpRequestHandler的handleRequest方法處理請求,邏輯比較簡單,獲取請求轉(zhuǎn)發(fā)器進(jìn)行請求轉(zhuǎn)發(fā)交給tomcat默認(rèn)的servlet來進(jìn)行處理 

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

		Assert.state(this.servletContext != null, "No ServletContext set");
		RequestDispatcher rd = this.servletContext.getNamedDispatcher(this.defaultServletName);
		if (rd == null) {
			throw new IllegalStateException("A RequestDispatcher could not be located for the default servlet '" +
					this.defaultServletName + "'");
		}
		rd.forward(request, response);
	}

2. 在java配置文件中配置ResourceHttpRequestHandler來進(jìn)行處理 

@Configuration
@EnableWebMvc
public class MyMvcConfigurer implements WebMvcConfigurer {

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**").addResourceLocations("/static/");
  }
}

和第一種配置幾乎一樣,其實只是換了一個handler類型來處理請求罷了

上述配置完成后org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping 方法會生成一個類名為SimpleUrlHandlerMapping的bean,當(dāng)其他handlerMapping無法處理請求時會接著調(diào)用SimpleUrlHandlerMapping對象進(jìn)行處理

ResourceHttpRequestHandler比DefaultServletHttpRequestHandler的構(gòu)建稍微復(fù)雜一點

org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#resourceHandlerMapping
/**
 * Return a handler mapping ordered at Integer.MAX_VALUE-1 with mapped
 * resource handlers. To configure resource handling, override
 * {@link #addResourceHandlers}.
 */
@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;
}


org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry#getHandlerMapping
/**
 * Return a handler mapping with the mapped resource handlers; or {@code null} in case
 * of no registrations.
 */
@Nullable
protected AbstractHandlerMapping getHandlerMapping() {
  if (this.registrations.isEmpty()) {
    return null;
  }

  Map<String, HttpRequestHandler> urlMap = new LinkedHashMap<>();
  for (ResourceHandlerRegistration registration : this.registrations) {
    for (String pathPattern : registration.getPathPatterns()) {
      ResourceHttpRequestHandler handler = registration.getRequestHandler();
      if (this.pathHelper != null) {
        handler.setUrlPathHelper(this.pathHelper);
      }
      if (this.contentNegotiationManager != null) {
        handler.setContentNegotiationManager(this.contentNegotiationManager);
      }
      handler.setServletContext(this.servletContext);
      handler.setApplicationContext(this.applicationContext);
      try {
        handler.afterPropertiesSet();
      }
      catch (Throwable ex) {
        throw new BeanInitializationException("Failed to init ResourceHttpRequestHandler", ex);
      }
      urlMap.put(pathPattern, handler);
    }
  }

  SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
  handlerMapping.setOrder(order);
  handlerMapping.setUrlMap(urlMap);
  return handlerMapping;
}

 之后也是調(diào)用SimpleUrlHandlerMapping相同的邏輯先根據(jù)請求路徑匹配找到對應(yīng)處理的handler,這里對應(yīng)的是ResourceHttpRequestHandler之后調(diào)用handleRequest方法,原理是先根據(jù)請求的路徑找到對應(yīng)的資源文件,再獲取資源文件的輸入流寫入到response響應(yīng)中,源碼如下:

org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest

?/**
 * Processes a resource request.
 * <p>Checks for the existence of the requested resource in the configured list of locations.
 * If the resource does not exist, a {@code 404} response will be returned to the client.
 * If the resource exists, the request will be checked for the presence of the
 * {@code Last-Modified} header, and its value will be compared against the last-modified
 * timestamp of the given resource, returning a {@code 304} status code if the
 * {@code Last-Modified} value  is greater. If the resource is newer than the
 * {@code Last-Modified} value, or the header is not present, the content resource
 * of the resource will be written to the response with caching headers
 * set to expire one year in the future.
 */
@Override
public void handleRequest(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

  // For very general mappings (e.g. "/") we need to check 404 first
  // 根據(jù)請求的文件路徑找到對應(yīng)的資源文件
  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
  // 校驗支持的方法GET和HEAD 以及驗證session是否必須
  checkRequest(request);

  // Header phase
  if (new ServletWebRequest(request, response).checkNotModified(resource.lastModified())) {
    logger.trace("Resource not modified - returning 304");
    return;
  }

  // Apply cache settings, if any
  // 可以根據(jù)設(shè)置的秒數(shù)設(shè)置緩存時間 cache-control:max-age=xxx
  prepareResponse(response);

  // Check the media type for the resource
  // 根據(jù)文件后綴去尋找 png -> image/png
  MediaType mediaType = getMediaType(request, resource);
  if (mediaType != null) {
    if (logger.isTraceEnabled()) {
      logger.trace("Determined media type '" + mediaType + "' for " + resource);
    }
  }
  else {
    if (logger.isTraceEnabled()) {
      logger.trace("No media type found for " + resource + " - not sending a content-type header");
    }
  }

  // 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) {
    Assert.state(this.resourceHttpMessageConverter != null, "Not initialized");
    // 設(shè)置content-type、content-length等響應(yīng)頭
    setHeaders(response, resource, mediaType);
    // 將文件流寫入到response響應(yīng)中
    this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage);
  }
  else {
    Assert.state(this.resourceRegionHttpMessageConverter != null, "Not initialized");
    response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
    ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
    try {
      List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
      response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
      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);
    }
  }
}

3.  web.xml配置servlet映射 

原理可以參考上一篇文章 

  <servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/static/*</url-pattern>
  </servlet-mapping>

將帶有/static/xxx 路徑的請求直接交給tomcat默認(rèn)的servlet去進(jìn)行處理 

最后

完成上述的一種配置后就能訪問到我們的靜態(tài)資源了,請求路徑http://localhost:8082/zxq/static/login.png

補(bǔ)充

若配置了攔截器且使用第二種方法,攔截器也會對靜態(tài)資源進(jìn)行攔截,若不需要攔截還需要進(jìn)行額外的配置去除比較麻煩

第一種方法用dispatcherServlet攔截所有的請求再將請求交給tomcat默認(rèn)的servlet處理,性能上有所消耗,攔截器不過濾

第二種方法攔截器會進(jìn)行過濾若需要過濾的路徑較多配置麻煩 

第三種方法直接用tomcat默認(rèn)的servlet進(jìn)行處理,但靜態(tài)資源路徑有多個時配置也比較麻煩 

綜上所述,根據(jù)自己項目的情況選擇哪一種方法~ 

@Configuration
@EnableWebMvc
public class MyMvcConfigurer implements WebMvcConfigurer {

  @Resource
  private CustomInterceptor customInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(customInterceptor)
        .addPathPatterns("/**")
        .excludePathPatterns("/static/**");
  }

//  @Override
//  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//    // tomcat默認(rèn)處理靜態(tài)資源的servlet名稱為default,不指定也可以DefaultServletHttpRequestHandler.setServletContext會自動獲取
////    configurer.enable("default");
//    configurer.enable();
//  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/static/**").addResourceLocations("/static/");
  }
}

以上就是SpringMVC靜態(tài)資源配置過程詳解的詳細(xì)內(nèi)容,更多關(guān)于SpringMVC靜態(tài)資源配置的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java編程刪除鏈表中重復(fù)的節(jié)點問題解決思路及源碼分享

    Java編程刪除鏈表中重復(fù)的節(jié)點問題解決思路及源碼分享

    這篇文章主要介紹了Java編程刪除鏈表中重復(fù)的節(jié)點問題解決思路及源碼分享,具有一定參考價值,這里分享給大家,供需要的朋友了解。
    2017-10-10
  • springboot 整合 sa-token簡介及入門教程

    springboot 整合 sa-token簡介及入門教程

    Sa-Token 是一個輕量級 Java 權(quán)限認(rèn)證框架,主要解決:登錄認(rèn)證、權(quán)限認(rèn)證、Session會話、單點登錄、OAuth2.0、微服務(wù)網(wǎng)關(guān)鑒權(quán) 等一系列權(quán)限相關(guān)問題,這篇文章主要介紹了springboot 整合 sa-token簡介及入門教程,需要的朋友可以參考下
    2023-05-05
  • Java深度優(yōu)先遍歷解決排列組合問題詳解

    Java深度優(yōu)先遍歷解決排列組合問題詳解

    這篇文章主要介紹了Java深度優(yōu)先遍歷解決排列組合問題詳解,深度優(yōu)先搜索是遞歸過程,帶有回退操作,因此需要使用棧存儲訪問的路徑信息,當(dāng)訪問到的當(dāng)前頂點沒有可以前進(jìn)的鄰接頂點時,需要進(jìn)行出棧操作,將當(dāng)前位置回退至出棧元素位置,需要的朋友可以參考下
    2024-01-01
  • 解決異常處理問題:getReader()?has?already?been?called?for?this

    解決異常處理問題:getReader()?has?already?been?called?for?this

    這篇文章主要介紹了解決異常處理:getReader()?has?already?been?called?for?this問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • SpringMVC MVC架構(gòu)與Servlet使用詳解

    SpringMVC MVC架構(gòu)與Servlet使用詳解

    MVC設(shè)計模式一般指 MVC 框架,M(Model)指數(shù)據(jù)模型層,V(View)指視圖層,C(Controller)指控制層。使用 MVC 的目的是將 M 和 V 的實現(xiàn)代碼分離,使同一個程序可以有不同的表現(xiàn)形式。其中,View 的定義比較清晰,就是用戶界面
    2022-10-10
  • 詳解java枚舉用法及實例

    詳解java枚舉用法及實例

    這篇文章主要介紹了Java枚舉詳解及使用實例,本文直接給出實例代碼,需要的朋友可以參考下
    2017-04-04
  • Spring?Boot如何在加載bean時優(yōu)先選擇我

    Spring?Boot如何在加載bean時優(yōu)先選擇我

    這篇文章主要介紹了Spring?Boot如何在加載bean時優(yōu)先選擇我,在?Spring?Boot?應(yīng)用程序中,我們可以采取三種方式實現(xiàn)自己的?bean?優(yōu)先加載,本文通過實例代碼給大家詳細(xì)講解,需要的朋友可以參考下
    2023-03-03
  • RabbitMq消息防丟失功能實現(xiàn)方式講解

    RabbitMq消息防丟失功能實現(xiàn)方式講解

    這篇文章主要介紹了RabbitMq消息防丟失功能實現(xiàn),RabbitMQ中,消息丟失可以簡單的分為兩種:客戶端丟失和服務(wù)端丟失。針對這兩種消息丟失,RabbitMQ都給出了相應(yīng)的解決方案
    2023-01-01
  • spring boot 項目中使用thymeleaf模板的案例分析

    spring boot 項目中使用thymeleaf模板的案例分析

    這篇文章主要介紹了spring boot 項目中使用thymeleaf模板的案例分析,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-09-09
  • Spring security 如何開放 Swagger 訪問權(quán)限

    Spring security 如何開放 Swagger 訪問權(quán)限

    這篇文章主要介紹了Spring security 如何開放 Swagger 訪問權(quán)限操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09

最新評論