# SpringMVC 中的静态资源访问
# 后缀形式没有『静态资源访问』问题
如果你将 DispatcherServlet 的 url-pattern 配置成后缀形式,例如,*.do
,那么,DispatcherServlet 就只会处理特定形式的请求,而将静态资源的请求 URI『漏给』Servlet 容器提供的 Default Servlet 。
而 Servlet 容器的 Defautl Servlet 的处理逻辑是:将 URI 看作一个文件的路径名,在对应的位置去找这个文件,读取其内容,并将读到的内容发回给请求方。这也正是我们期望的对静态资源的处理方式。
因此,非 RESTful 风格的 Java Web 项目就采用这种方案。不要无缘无故去掉 URI 中的后缀,自找麻烦。
# / 和 /* 会遇到『静态资源访问』问题
当你将 DispatcherServlet 的 <url-pattern> 配置成 /
或 /*
时,会遇到静态资源访问问题。
不过,它俩的原因不太一样:
<url-pattern>/</url-pattern> 的原因
由于 url-pattern 设置成了
/
,因此,DispatcherServlet 的身份将变为『Default Servlet』,而 Servlet 容器自带的『Default Servlet』则不再起作用。但问题是,DispatcherServlet 的对 URI 的处理逻辑中又没有像 Servlet 容器自带的『Default Servlet』那样的处理静态资源的逻辑,那么最终,Dispatcher Servlet 最终无法对静态资源访问的 URI 做出正确处理,而导致 404 。
<url-pattern>/*</url-pattern> 的原因
由于 url-pattern 设置成了
/*
,而/*
的优先级和范畴又异常强大,因此,啥请求都走到了 DispatcherServelt 这里,包括静态资源请求。虽然,Servlet 容器自带的『Default Servlet』此时是存在的,但是没有任何请求会『漏到』它这里,它会『闲着无所事事』。
在 Servlet 容器自带的『Default Servlet』闲得蛋疼的同时,DispatcherServlet 的对 URI 的处理逻辑中又没有对静态资源的处理逻辑,那么最终,Dispatcher Servlet 最终无法对静态资源访问的 URI 做出正确处理,而导致 404 。
# 解决方案一
通过配置,让 DispatcherServlet 去『利用』Servlet 容器的自带的『Default Servlet』,这样,从外观上看,DispatcherServlet 就具备了处理静态资源的能力,自然也就解决了静态资源访问问题。
配置文件版:spring-web.xml
<mvc:default-servlet-handler />
考虑到并非所有的容器的默认的 DefaultSevlet 的 name 并非是 default ,所以在非 Tomcat 容器中,需要手动指定其 name 。
<mvc:default-servlet-handler default-servlet-name=“所使用的 Web 服务器默认使用的 Servlet 名称” />
Tomcat, Jetty, JBoss, and GlassFish:"default"
Google App Engine:"_ah_default"
WebLogic:"FileServlet"
WebSphere:"SimpleFileServlet"
Java 代码配置版:SprinbWebConfig.java
@Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); }
# 解决方案二
再通过配置(mvc:resources)『告诉』DispatcherServlet,哪些请求是静态资源请求,而不是 Servlet 请求。
假定项目的目录结构如下:
webapp
│── img
│ ├── ...
│ └── ...
│── js
│ ├── ...
│ └── ...
│── css
│ ├── ...
│ └── ...
└── WEB-INF
└── jsp
├── ...
└── ...
.jsp 页面类似如下:
<link rel="stylesheet" href="${pageContext.request.contextPath}/css/bootstrap.min.css">
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery.2.1.1.min.js"></script>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/bootstrap.min.js"></script>
我们需要表达这样的逻辑:但凡以 /xxx 开始的请求,都是针对 /xxx/ 目录下的静态资源的访问。
配置文件版:spring-web.xml
<mvc:resources location="/xxx/" mapping="/xxx/**" />
location 元素表示 webapp 目录下的 xxx 目录;mapping 元素表示以 /xxx 开头的所有请求路径。
例如:
<mvc:resources mapping="/img/**" location="/img/" /> <mvc:resources mapping="/js/**" location="/js/" /> <mvc:resources mapping="/css/**" location="/css/" />
代码配置版:SpringWebConfig.java
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry .addResourceHandler("/img/**") .addResourceLocations("classpath:/img/") .addResourceHandler("/js/**") .addResourceLocations("classpath:/js/") .addResourceHandler("/css/**") .addResourceLocations("classpath:/css/"); // classpath: 可省略 }