如果你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题。如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg的访问也就被拦截了。
目的:可以正常访问静态文件,不要找不到静态文件报404。
方案一:激活Tomcat的defaultServlet来处理静态文件
Xml代码
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.jpg</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.js</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.css</url-pattern> </servlet-mapping>
要写在DispatcherServlet的前面, 让 defaultServlet先拦截,这个就不会进入Spring了。
方案二: 在spring3.0.4以后版本提供了mvc:resources 的使用方法:
Xml代码
<!-- 对静态资源文件的访问 --> <mvc:resources mapping="/images/**" location="/images/" />
/images/**映射到ResourceHttpRequestHandler进行处理,location指定静态资源的位置.可以是web application根目录下、jar包里面,这样可以把静态资源压缩到jar包中。cache-period 可以使得静态资源进行web cache 如果出现下面的错误,可能是没有配置<mvc:annotation-driven />的原因。 报错WARNING: No mapping found for HTTP request with URI [/mvc/user/findUser/lisi/770] in DispatcherServlet with name ‘springMVC‘
使用<mvc:resources/>元素,把mapping的URI注册到SimpleUrlHandlerMapping的urlMap中,key为mapping的URI pattern值,而value为ResourceHttpRequestHandler,这样就巧妙的把对静态资源的访问由HandlerMapping转到ResourceHttpRequestHandler处理并返回,所以就支持classpath目录,jar包内静态资源的访问.另外需要注意的一点是,不要对SimpleUrlHandlerMapping设置defaultHandler。因为对static uri的defaultHandler就是ResourceHttpRequestHandler,否则无法处理static resources request。
方案三 ,使用<mvc:default-servlet-handler/>
Xml代码
<mvc:default-servlet-handler/>
会把"/**" url,注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler处理并返回DefaultServletHttpRequestHandler使用,就是各个Servlet容器自己的默认Servlet。
补充说明:多个HandlerMapping的执行顺序问题:
DefaultAnnotationHandlerMapping的order属性值是:0
<mvc:resources/ >自动注册的SimpleUrlHandlerMapping的order属性值是:2147483646 <mvc:default-servlet-handler/>自动注册的SimpleUrlHandlerMapping的order属性值是:2147483647
spring会先执行order值比较小的。当访问一个a.jpg图片文件时,先通过DefaultAnnotationHandlerMapping来找处理器,一定是找不到的,我们没有叫a.jpg的Action。再按order值升序找,由于最后一个SimpleUrlHandlerMapping是匹配"/**"的,所以一定会匹配上,再响应图片。
注:如果DispatcherServlet拦截 *.do这样的URL,就不存上述问题了。
方案四、认真阅读spring的源码,好好理解一下。
17.16.7 Serving of Resources
This option allows static resource requests following a particular URL pattern to be served by a ResourceHttpRequestHandler from any of a list of Resource locations. This provides a convenient way to serve static resources from locations other than the web application root, including locations on the classpath. The cache-period property may be used to set far future expiration headers (1 year is the recommendation of optimization tools such as Page Speed and YSlow) so that they will be more efficiently utilized by the client. The handler also properly evaluates the Last-Modified header (if present) so that a 304 status code will be returned as appropriate, avoiding unnecessary overhead for resources that are already cached by the client. For example, to serve resource requests with a URL pattern of /resources/** from a public-resources directory within the web application root you would use:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/"); } }
And the same in XML:
<mvc:resources mapping="/resources/**" location="/public-resources/"/>
To serve these resources with a 1-year future expiration to ensure maximum use of the browser cache and a reduction in HTTP requests made by the browser:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**").addResourceLocations("/public-resources/").setCachePeriod(31556926); } }
And in XML:
<mvc:resources mapping="/resources/**" location="/public-resources/" cache-period="31556926"/>
The mapping attribute must be an Ant pattern that can be used by SimpleUrlHandlerMapping, and the location attribute must specify one or more valid resource directory locations. Multiple resource locations may be specified using a comma-separated list of values. The locations specified will be checked in the specified order for the presence of the resource for any given request. For example, to enable the serving of resources from both the web application root and from a known path of /META-INF/public-web-resources/ in any jar on the classpath use:
@EnableWebMvc @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/", "classpath:/META-INF/public-web-resources/"); } }
And in XML:
<mvc:resources mapping="/resources/**" location="/, classpath:/META-INF/public-web-resources/"/>
When serving resources that may change when a new version of the application is deployed it is recommended that you incorporate a version string into the mapping pattern used to request the resources so that you may force clients to request the newly deployed version of your application’s resources. Support for versioned URLs is built into the framework and can be enabled by configuring a resource chain on the resource handler. The chain consists of one more ResourceResolver instances followed by one or more ResourceTransformer instances. Together they can provide arbitrary resolution and transformation of resources.
The built-in VersionResourceResolver can be configured with different strategies. For example a FixedVersionStrategy can use a property, a date, or other as the version. A ContentVersionStrategy uses an MD5 hash computed from the content of the resource (known as "fingerprinting" URLs).
ContentVersionStrategy is a good default choice to use except in cases where it cannot be used (e.g. with JavaScript module loaders). You can configure different version strategies against different patterns as shown below. Keep in mind also that computing content-based versions is expensive and therefore resource chain caching should be enabled in production.
Java config example;
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/resources/**") .addResourceLocations("/public-resources/") .resourceChain(true).addResolver( new VersionResourceResolver().addContentVersionStrategy("/**")); } }
XML example:
<mvc:resources mapping="/resources/**" location="/public-resources/"> <mvc:resource-chain> <mvc:resource-cache /> <mvc:resolvers> <mvc:version-resolver> <mvc:content-version-strategy patterns="/**"/> </mvc:version-resolver> </mvc:resolvers> </mvc:resource-chain> </mvc:resources>
In order for the above to work the application must also render URLs with versions. The easiest way to do that is to configure the ResourceUrlEncodingFilter which wraps the response and overrides its encodeURL method. This will work in JSPs, FreeMarker, Velocity, and any other view technology that calls the response encodeURL method. Alternatively, an application can also inject and use directly the ResourceUrlProvider bean, which is automatically declared with the MVC Java config and the MVC namespace.
17.16.8 Falling Back On the "Default" Servlet To Serve Resources
This allows for mapping the DispatcherServlet to "/" (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures a DefaultServletHttpRequestHandler with a URL mapping of "/**" and the lowest priority relative to other URL mappings.
This handler will forward all requests to the default Servlet. Therefore it is important that it remains last in the order of all other URL HandlerMappings. That will be the case if you use <mvc:annotation-driven> or alternatively if you are setting up your own customized HandlerMapping instance be sure to set its order property to a value lower than that of the DefaultServletHttpRequestHandler, which is Integer.MAX_VALUE.
To enable the feature using the default setup use:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable(); } }
Or in XML:
<mvc:default-servlet-handler/>
The caveat to overriding the "/" Servlet mapping is that the RequestDispatcher for the default Servlet must be retrieved by name rather than by path. The DefaultServletHttpRequestHandler will attempt to auto-detect the default Servlet for the container at startup time, using a list of known names for most of the major Servlet containers (including Tomcat, Jetty, GlassFish, JBoss, Resin, WebLogic, and WebSphere). If the default Servlet has been custom configured with a different name, or if a different Servlet container is being used where the default Servlet name is unknown, then the default Servlet’s name must be explicitly provided as in the following example:
@Configuration @EnableWebMvc public class WebConfig extends WebMvcConfigurerAdapter { @Override public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { configurer.enable("myCustomDefaultServlet"); } }
Or in XML:
<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>