SpringMVC会在dispatchServlet拦截请求,所有在我们配置的时候,如果将所有请求都拦截了,连同静态资源也会拦截。
有两种方式对静态资源进行访问:
1、配置default-servlet-handler,SpringMVC上下文中定义一个DefaultServletHttpRequestHandler,它会检查DispatchServlet的URL。然后如果发现是静态资源,就由web应用服务器的默认Servlet处理,如果是其他请求就直接交由相应的handler处理。
2、使用mvc:resources配置,定义相应的静态资源映射。
配置default-servlet-handler非常简单,就一行配置就完成了:
<mvc:default-servlet-handler />
使用mvc:resources配置也不复杂:
<mvc:resources mapping="/resource" location="/" /> mapping是访问的路径,location是实际的访问资源的位置。
例如我想访问/image/abc.jpg 在客户端输入的路径就为/resource/image/abc.jpg
静态资源缓存
我们可以使用静态缓存cache-period,但是缓存之后,如果我们的静态资源文件更新了,用户也是用旧缓存的,只要未达到过期时间。
<mvc:resources cache-period="2592000" mapping="/#{re.resourcePath}/**" location="/"/>
所以我们希望在我们更新静态资源后,用户会使用新的静态资源,所以我们需要采用动态的mapping资源映射目录。
public class ResourcePathExposer implements ServletContextAware { private ServletContext servletContext; private String resourcePath; public void init(){ String version = "1.0"; String path = "/resource"; this.resourcePath = path + "-" + version; this.servletContext.setAttribute("resourcePath",this.resourcePath); } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } public ServletContext getServletContext() { return servletContext; } public String getResourcePath() { return resourcePath; } public void setResourcePath(String resourcePath) { this.resourcePath = resourcePath; } }实现ServletContextAware接口获得ServletContext,然后通过ServletContext将映射目录进行动态定义。这里现在的目录为/resource-1.0,如果我们需要更改静态文件,就直接修改为1.1或者其他即可。然后将这个类配置为MVC的bean,然后配置mvc:resources的时候使用SpringEL表达式获得这个bean的resourcePath属性:
<bean id="re" class="com.maxfunner.web.ResourcePathExposer" init-method="init"/> <mvc:resources cache-period="2592000" mapping="/#{re.resourcePath}/**" location="/"/>因为我们在ResourcePathExposer 将resourcePath 这个属性写到了context的attribute当中。所以在JSP页面可以直接获得这个属性的值:
<img src="<c:url value="/" />${applicationScope.resourcePath}/image/${imagePath}"/>
这样我们就能又做到缓存也能够做到更新最后不读取缓存的静态资源。
SpringMVC Intercepter 拦截器
SpringMVC 的Intercepter 是一个相对比较简单的功能,主要就完成拦截的工作任务,实现拦截器需要实现HandlerInterceptor接口,里面有三大方法。
public class TestIntercepter implements HandlerInterceptor { //在调用handler处理方法之前调用,参数有request response 和 handler 如果返回false则不调用到handler当中 public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { System.out.println("(uri:"+httpServletRequest.getRequestURI()+")>>>>>>>preHandle:"+handler.getClass().getName()); if(httpServletRequest.getRequestURI().equals("/springreview/user/main.html")){ httpServletResponse.sendRedirect("/springreview/user/login.html"); return false; } Map<String, String[]> params = httpServletRequest.getParameterMap(); Set<String> keySets = params.keySet(); for (String key : keySets) { System.out.print(key + "->"); String[] values = params.get(key); for (String valueItem : values) { System.out.print(" " + valueItem); } System.out.println(""); } return true; } //在调用handler之后执行,参数除了有request response 和 handler之外还提供了handler所返回的modelAndView public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, ModelAndView modelAndView) throws Exception { modelAndView.addObject("IntercepterMessage", "TEST"); System.out.println(">>>>>>>postHandle:"+handler.getClass().getName()); } //在渲染完View之后执行 public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
相关的三个方法已经提供了相应的注释,但是方法的内容我一会再说,先看看配置文件对interceptor的配置:
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/user/*"/> <bean class="com.maxfunner.web.TestIntercepter"/> </mvc:interceptor> </mvc:interceptors>这里可以看就到mapping是代表所需要拦截的目标uri,而bean就是interceptor的实现类,当然我们可以添加多个interceptor 执行的顺序跟<mvc:interceptor>定义的顺序一致。preHandle是由上至下执行 postHandle是由下至上执行。这个跟filter的执行一致。
在preHandle这个方法当中我们通过request对象getParameterMap获得所有请求参数。如果我们对参数提交检查失败或者某些页面需要特定的权限或这个请求没有允许登入,我们可以使用response对象sendRedirect到指定的页面当中,并返回false 代表拦截,并不继续执行其余的interceptor和目标的handler。如果我们sendRedirect之后返回true,会报需要注意。
postHandle方法可以在modelAndView添加一些模型属性 进行相应的渲染,甚至改变其viewName。