logo头像

不破不立

SpringMVC拦截器及静态资源处理方法

本文讲解一下SpringMVC拦截器的工作原理、如何使用以及针对静态资源放行的三种处理方法的相关问题。

拦截器简介

  拦截器是指通过统一拦截从浏览器(或其他应用)发往服务器的请求来完成功能的增强。
  使用场景:解决请求的共性问题(如:乱码问题、权限验证问题)

  介绍拦截器的基本工作原理之前,先介绍一个例子:SpringMVC配置过滤器来解决乱码问题
  这个问题相信大家很熟悉,解决方法也是很简单,就是在web.xml里面配置一个filter,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- encoding -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

  进行如此配置,就解决了请求传中文参数乱码问题了。而SpringMVC的拦截器和过滤器非常相似。

拦截器的实现

  1. 实现HandlerInterceptor接口(还有实现WebRequestInterceptor接口,不过不建议),会默认要求实现3个方法,根据需求进行设计;如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /*此处还有可以实现WebRequestInterceptor接口,不过其实现的preHandle方法没有返回值,也就是不能终止请求,其注册配置方法也是一样*/
    @Slf4j
    public class TestInterceptor implements HandlerInterceptor{

    /**
    * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,
    * SpringMVC中的Interceptor拦截器是链式的,可以同时存在多个Interceptor,
    * 然后SpringMVC会根据声明的前后顺序一个接一个的执行,
    * 而且所有的Interceptor中的preHandle方法都会在Controller方法调用之前调用。
    * SpringMVC的这种Interceptor链式结构也是可以进行中断的,
    * 这种中断方式是令preHandle的返回值为false,当preHandle的返回值为false的时候整个请求就结束了。
    * 参数Object表示被拦截的请求的目标对象
    */
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
    log.debug("执行到preHandle方法");
    return true;
    }

    /**
    * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。
    * postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之 后, 也就是在Controller的方法调用之后执行,
    * 但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操作。
    * 这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,
    * 这跟Struts2里面的拦截器的执行过程有点像,
    * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,
    * Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor或者是调用action,
    * 然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。
    */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
    log.debug("执行到postHandle方法");
    }
    /**
    * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。
    * 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 这个方法的主要作用是用于清理资源的,
    */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
    log.debug("执行到afterCompletion方法");
    }
    }
  2. 在SpringMVC配置中注册;

  3. 配置拦截规则,根据需求进行设计。
      2,3步的实现如下:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <mvc:interceptors>
    <mvc:interceptor>
    <!-- 规则:
    1. /**拦截多级,如/test/hello
    2. /*拦截一级,如/test,不会拦截/test/**请求
    -->
    <mvc:mapping path="/**"/>
    <bean class="com.panhainan.interceptor.TestInterceptor"/>
    </mvc:interceptor>
    </mvc:interceptors>

拦截器和过滤器的区别

过滤器Filter依赖于Servlet容器,基于回调函数,过滤范围大;
拦截器Interceptor依赖于框架容器,基于反射容器,只过滤请求。

SpringMVC针对静态资源如js,css等文件的三种处理方式:

方法一:使用 mvc:resources

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<mvc:resources location="/" mapping="/**/*.js"/>  
<mvc:resources location="/" mapping="/**/*.css"/>
<mvc:resources location="/assets/" mapping="/assets/**/*"/>
<mvc:resources location="/images/" mapping="/images/*" cache-period="360000"/>

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<mvc:exclude-mapping path="/**/fonts/*"/>
<mvc:exclude-mapping path="/**/*.css"/>
<mvc:exclude-mapping path="/**/*.js"/>
<mvc:exclude-mapping path="/**/*.png"/>
<mvc:exclude-mapping path="/**/*.gif"/>
<mvc:exclude-mapping path="/**/*.jpg"/>
<mvc:exclude-mapping path="/**/*.jpeg"/>
<bean class="com.panhainan.interceptor.TestInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>

方法二:使用默认的静态资源处理Servlet处理静态资源

  在spring-mvc.xml中启用默认Servlet

1
<mvc:default-servlet-handler/>

  在web.xml中增加对静态资源的处理,但是当前的设置必须在Spring的Dispatcher的前面

1
2
3
4
5
6
7
<servlet-mapping>    
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>/assets/*"</url-pattern>
<url-pattern>/images/*</url-pattern>
</servlet-mapping>

方法三:修改Spring的全局拦截设置为*.do的拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>

三种方案的优劣分析

  • 第一种方案配置比较臃肿,多个拦截器时增加文件行数,不推荐使用;
  • 第二种方案使用默认的Servlet进行资源文件的访问,Spring拦截所有请求,然后再将资源文件交由默认的Servlet进行处理,性能上少有损耗;
  • 第三种方案Spring只是处理以”.do”结尾的访问,性能上更加高效,但是再访问路径上必须都以“.do”结尾,URL不太文雅。
    综上所述,推荐使用第二和第三种方案

参考文章

SpringMVC 拦截器不拦截静态资源的三种处理方式

SpringMVC之mvc:interceptors拦截器的用法

  本文的代码可以在github上找到,地址为:github.com/panhainan/spring-family

上一篇

评论系统未开启,无法评论!

如果有好的建议或疑问等可以发送邮件至:panhainan@yeah.net,或者添加QQ:1016593477,将你的建议或者疑问告诉作者,作者会对你的建议进行处理并补充到文章的尾部,谢谢大家的谅解!