目录
springmvc静态资源配置1. 在java配置文件中配置DefaultServletHttpRequestHandler来进行处理2. 在java配置文件中配置ResourceHttpRequestHandler来进行处理3. web.xml配置servlet映射最后补充springmvc静态资源配置
dispatcher org.springframework.web.servlet.DispatcherServlet false dispatcher /
在javaweb项目中配置了DispatcherServlet的情况下,如果不进行额外配置的话,几乎所有的请求都会走这个servlet来处理,默认静态资源按路径是访问不到的会报404错误,下面讲一讲如何配置才能访问到静态资源,本文将介绍三种方法
1. 在java配置文件中配置DefaultServletHttpRequestHandler来进行处理
@Configuration
@EnableWebMvc
public class MyMvcConfigurer implements WebMvcConfigurer {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
// tomcat默认处理静态资源的servlet名称为default,不指定也可以DefaultServletHttpRequestHandler.setServletContext会自动获取
// configurer.enable("default");
configurer.enable();
}
}上述配置完成后org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport#defaultServletHandlerMapping方法会生成一个类名为SimpleUrlHandlerMapping的bean,当其他handlerMapping无法处理请求时会接着调用SimpleUrlHandlerMapping对象进行处理
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匹配完成后会调用handler处理请求
(资料图)
下面这个方法主要用来匹配获取handler
org.springframework.web.servlet.handler.AbstractUrlHandlerMapping#getHandlerInternal
@Override
@Nullable
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 请求静态资源 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. *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. *
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
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 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 uriTemplateVariables = new LinkedHashMap<>(); for (String matchingPattern : matchingPatterns) { if (patternComparator.compare(bestMatch, matchingPattern) == 0) { Map vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath); Map 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; }
接着调用DefaultServletHttpRequestHandler的handleRequest方法处理请求,逻辑比较简单,获取请求转发器进行请求转发交给tomcat默认的servlet来进行处理
@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来进行处理
@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,当其他handlerMapping无法处理请求时会接着调用SimpleUrlHandlerMapping对象进行处理
ResourceHttpRequestHandler比DefaultServletHttpRequestHandler的构建稍微复杂一点
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 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;
} 之后也是调用SimpleUrlHandlerMapping相同的逻辑先根据请求路径匹配找到对应处理的handler,这里对应的是ResourceHttpRequestHandler之后调用handleRequest方法,原理是先根据请求的路径找到对应的资源文件,再获取资源文件的输入流写入到response响应中,源码如下:
org.springframework.web.servlet.resource.ResourceHttpRequestHandler#handleRequest
/** * Processes a resource request. *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 // 根据请求的文件路径找到对应的资源文件 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 // 可以根据设置的秒数设置缓存时间 cache-control:max-age=xxx prepareResponse(response); // Check the media type for the resource // 根据文件后缀去寻找 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"); // 设置content-type、content-length等响应头 setHeaders(response, resource, mediaType); // 将文件流写入到response响应中 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
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映射
原理可以参考上一篇文章
default /static/*
将带有/static/xxx 路径的请求直接交给tomcat默认的servlet去进行处理
最后
完成上述的一种配置后就能访问到我们的静态资源了,请求路径http://localhost:8082/zxq/static/login.png
补充
若配置了拦截器且使用第二种方法,拦截器也会对静态资源进行拦截,若不需要拦截还需要进行额外的配置去除比较麻烦
第一种方法用dispatcherServlet拦截所有的请求再将请求交给tomcat默认的servlet处理,性能上有所消耗,拦截器不过滤
第二种方法拦截器会进行过滤若需要过滤的路径较多配置麻烦
第三种方法直接用tomcat默认的servlet进行处理,但静态资源路径有多个时配置也比较麻烦
综上所述,根据自己项目的情况选择哪一种方法~
@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默认处理静态资源的servlet名称为default,不指定也可以DefaultServletHttpRequestHandler.setServletContext会自动获取
//// configurer.enable("default");
// configurer.enable();
// }
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}以上就是SpringMVC静态资源配置过程详解的详细内容,更多关于SpringMVC静态资源配置的资料请关注脚本之家其它相关文章!
X 关闭
X 关闭
- 1亚马逊开始大规模推广掌纹支付技术 顾客可使用“挥手付”结账
- 2现代和起亚上半年出口20万辆新能源汽车同比增长30.6%
- 3如何让居民5分钟使用到各种设施?沙特“线性城市”来了
- 4AMD实现连续8个季度的增长 季度营收首次突破60亿美元利润更是翻倍
- 5转转集团发布2022年二季度手机行情报告:二手市场“飘香”
- 6充电宝100Wh等于多少毫安?铁路旅客禁止、限制携带和托运物品目录
- 7好消息!京东与腾讯续签三年战略合作协议 加强技术创新与供应链服务
- 8名创优品拟通过香港IPO全球发售4100万股 全球发售所得款项有什么用处?
- 9亚马逊云科技成立量子网络中心致力解决量子计算领域的挑战
- 10京东绿色建材线上平台上线 新增用户70%来自下沉市场

