springmvc静态资源配置
<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的情况下,如果不进行额外配置的话,几乎所有的请求都会走这个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.
* <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;
}
接着调用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<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;
}
之后也是调用SimpleUrlHandlerMapping相同的逻辑先根据请求路径匹配找到对应处理的handler,这里对应的是ResourceHttpRequestHandler之后调用handleRequest方法,原理是先根据请求的路径找到对应的资源文件,再获取资源文件的输入流写入到response响应中,源码如下:
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
// 根据请求的文件路径找到对应的资源文件
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<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默认的servlet去进行处理
最后
完成上述的一种配置后就能访问到我们的静态资源了,请求路径http://localhost:8082/zxq/static/login.png
springmvc静态资源配置的更多相关文章
- Springboot系列(四)web静态资源配置详解
Springboot系列(四)web静态资源配置 往期精彩 SpringBoot系列(一)idea新建Springboot项目 SpringBoot系列(二)入门知识 SpringBoot系列(三)配 ...
- nginx代理配置 配置中的静态资源配置,root 和 alias的区别。启动注意事项
这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...
- nginx代理配置 配置中的静态资源配置,root 和 alias的区别
这篇主要内容是:nginx代理配置 配置中的静态资源配置,root 和 alias的区别.启动注意事项! 为什么会在window上配置了nginx呢?最近我们的项目是静态资源单独放在一个工程里面,后端 ...
- spring boot mvc系列-静态资源配置与MappingHandler拦截器
静态资源配置 Spring Boot 默认将 /** 所有访问映射到以下目录: classpath:/static classpath:/public classpath:/resources cla ...
- springMVC 静态资源加版本号
springMVC 静态资源加版本号 http://blog.csdn.net/zhangt85/article/details/42126275
- Springboot项目静态资源配置
springboot项目的静态资源配置网上有好多,说的也很详细 我今天出错是自定义了一个filter,在shiro里配置的/**,自定义filter 所以一直报302
- springmvc静态资源;mvc:default-servlet-handler后Controller失效
springmvc静态资源;mvc:default-servlet-handler后Controller失效 web.xml配置<url-pattern>/</url-pattern ...
- springboot(八)内置SpringMvc静态文件地址修改
参考:作者:恒宇少年链接:https://www.jianshu.com/p/c6ab1081fd5f 介绍: SpringMVC大家都不陌生,而被SpringBoot集成的SpringMVC除了 ...
- Django_静态资源配置和ajax(九)
一.静态资源配置 静态资源的相关配置都在项目目录下的 settings.py 文件中进行配置.配置参数如下: # 浏览器访问静态资源时的路径 STATIC_URL = '/static2/' # 存放 ...
随机推荐
- Docker权限 “Got permission denied while trying to connect to the Docker daemon socket at unix:///var/”
问题及解决办法 在普通用户下执行docker命令需要用sudo,没加sudo出现了下图所示的提示: 从上图看出,权限不足连接/var/run/docker.sock,我们看下这个文件: 可以看出,这个 ...
- 《Java基础——IO流》
Java基础--IO流 一.字节流: 1.输入流 (InputStream) 规则: 此处用于读取txt文件中的内容. 代码: import java.io.*; public c ...
- Vue几行代码实现搜索功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- Canal搭建
目录 搭建环境 MySQL环境 Canal环境 客户端 总结 需求:解决私有环境数据库的基础数据同步问题,每当中心库基础数据发生改变时,其他私有库都会增量同步 Canal主要用途是基于 MySQL 数 ...
- hive完整搭建
安装 MySQL 服务的详细步骤如下: 1. 解压安装包 现在已经为大家下载好了 MySQL 5.7.25 的安装包,存放在 /root/software 目录下.首先进入此目录下,然后使用tar - ...
- jenkins+k8s部署
1. jenkins配置部署,从git或者svn等拉取代码,编制打包,在博客的jenkins那块有具体的讲解 2. 将打好的包war/java包拷贝到k8s-Master,或者在jenkins上也行, ...
- UiPath文本操作Set Text的介绍和使用
一.Set Text的介绍 向输入框/文本框写入文本的一种操作 二.Set Text在UiPath中的使用 1.打开设计器,在设计库中新建一个Sequence,为序列命名及设置Sequence存放的路 ...
- Could not transfer artifact xxx from/to xxx解决方案
maven中默认的镜像加载是这个 在setting.xml文件中 <mirror> <id>nexus</id> <mirrorOf>*</mir ...
- .Net 应用考虑x64生成
在x86架构下使用32位应用程序,理论上32位能够分配4G的大小空间,但实际上默认 .NET Framework 可以创建一个不超过 2 GB 的对象,在实际使用过程中可能使用到1.5G的样子就会报内 ...
- nodejs - http.request是否有超时
默认没有.那么,req.setTimeout(msec, callback)是干什么用的. 它的意思是 socket msec 没有活动后执行callback,不帮你关闭连接. 就像一个秒表,每收到数 ...