# Spring MVC 的拦截器
Spring MVC 中的拦截器( Interceptor )类似于 Servlet 中的过滤器( Filter ),它主要用于拦截用户请求并做出相应的处理。
# 拦截器的定义
通过直接(或间接)实现 HandlerInterceptor 接口,并向 Spring MVC 注册既可使用自定义拦截器。
我们一般都是通过继承 HandlerInterceptorAdapter 类来间接实现 HandlerInterceptor 接口。
我们可以去重写 HandlerInterceptorAdapter 的 3 个方法:
.preHandle 方法在业务处理器处理请求之前被调用
该方法会在 Controller 方法前执行
返回值表示是否继续后续操作:
返回 true 时,表示继续向下执行;
返回 false 时,会中断后续所有操作(包括调用下一个拦截器和 Controller 中的方法执行)。
.postHandle 在业务处理器处理请求执行完成后,生成视图之前执行
在 Controller 方法调用之后,且解析视图之前执行。
可以通过此方法对模型和视图做出进一步修改。
.afterCompletion 在 DispatcherServlet 完全处理完请求后被调用,可用于清理资源等。
该方法会在整个请求完成(即,视图渲染结束)之后执行。
可以通过此方法实现一些资源清理、记录日志信息等工作。
# 拦截器的配置
要使自定义的拦截器类生效,还需要在 Spring MVC 的配置中进行配置:
配置文件版:spring-web.xml
<mvc:interceptors> <bean class="com.hemiao.web.interceptor.MyInterceptor1"/> <!-- 1 --> <mvc:interceptor> <!-- 2 --> <mvc:mapping path="/**"/> <!-- 3 --> <mvc:exclude-mapping path=""/> <!-- 4 --> <bean class="com.hemiao.web.interceptor.MyInterceptor2"/> <!-- 5 --> </mvc:interceptor> <mvc:interceptor> <!-- 6 --> <mvc:mapping path="/hello"/> <!-- 7 --> <bean class="com.hemiao.web.interceptor.MyInterceptor3"/> <!-- 8 --> </mvc:interceptor> </mvc:interceptors>
如
1
所示,在 interceptors 下注册的拦截器是全局拦截器,会拦截所有请求如
2 | 6
所示,在 interceptor 下注册的拦截器是局部拦截器,需要明确配置该拦截器拦截哪些请求。如
3
所示,表示拦截所有路径如
4
所示,表示在拦截某些请求的前提下,排除、不拦截某些请求如
7
所示,表示拦截所有以 /hello 开头的路径
当设置多个拦截器时,先按顺序调用 .preHandle 方法,然后逆序调用每个拦截器的 .postHandle 和 .afterCompletion 方法,即:
A-pre
B-pre
C-pre
C-post, C-after
B-post, B-after
A-post, A-after
代码配置版:SpringWebConfig.java
@Configuration @EnableWebMvc @ComponentScan("xxx.yyy.zzz.web") public class SpringWebConfig implements WebMvcConfigurer { ... // 暂未验证 @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new MyInterceptor1()); registry.addInterceptor(new MyInterceptor2()) .addPathPatterns("/**") .order(1); registry.addInterceptor(new MyInterceptor3()) .addPathPatterns("/hello") .order(2); } ... }
# Spring MVC POST 请求乱码问题
Spring MVC 专门提供了一个 Filter 用于解决 POST 请求乱码问题,只需要在 Java Web 配置中配置使用即可:
.xml 配置版:webx.ml
<filter> <filter-name>characterEncodingFilter</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>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
代码配置版:MyWebInitializer.java
@Override protected Filter[] getServletFilters() { CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter("UTF-8", true); return new Filter[] { characterEncodingFilter }; }
TIP
关于 get 请求问题,一般是通过修改 tomcat 配置设置文件解决。
# 拦截器和自定义注解实现认证
自定义注解
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface RequiresAuthentication { }
拦截器
public class LoginInterceptor implements HandlerInterceptor { private static final String USERNAME_KEY = "username"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception { // 如果拦截的不是方法则直接通过(有可能用户请求的是 JSP) if (!(object instanceof HandlerMethod)) { return true; } // 从 session 请求头中取出 username String username = (String) request.getSession().getAttribute(USERNAME_KEY); HandlerMethod handlerMethod = (HandlerMethod) object; Method method = handlerMethod.getMethod(); // 代表,需要登录认证后才能进行的操作。即,游客无法进行该操作。 if (method.isAnnotationPresent(RequiresAuthentication.class)) { return !Strings.isNullOrEmpty(username); } // 游客/匿名用户 可以进行的操作 else { return true; } // return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }