SpringMVC 基础(三)
SpringMVC 基础(三)
SpringMVC 基础笔记系列
SpringMVC中的拦截器(Interceptor)
基本概念
在SpringMVC中的拦截器可以是运行在控制器(Controller)之前的组件,可以设置拦截器应用于哪些请求路径,当发生这些请求时,拦截器会自动执行,在执行过程中,可以对请求相关数据进行判断,选择阻止继续向后执行,或选择放行。
注意:拦截器是一个若干种请求都会经历的执行过程,但是,并不一定需要阻止继续运行,只要是若干种请求都需要做相同的事情,也许每种请求的处理过程都是选择放行,也可以使用拦截器。
开发拦截器
首先,所有的拦截器类都必须实现HandlerInterceptor
接口,可以自定义LoginInterceptor
:
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("LoginInterceptor.preHandle()");
return false;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("LoginInterceptor.postHandle()");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("LoginInterceptor.afterCompletion()");
}
}
在拦截器的3个方法中,只有preHandle()
方法是运行在控制器(Controller)之前的,另2个方法是运行在控制器之后的,所以,只有preHandle()
具有真正意义的“拦截”功能,该方法的返回值是boolean
类型的,当返回true
时表示放行,返回false
时将阻止继续向后执行,即控制器并不会被执行:
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("LoginInterceptor.preHandle()");
// 获取Session
HttpSession session = request.getSession();
// 检查Session中是否有登录信息
if (session.getAttribute("uid") == null) {
// 没有登录信息,重定向到登录页
response.sendRedirect("../user/login.do");
// 执行拦截
return false;
}
// 放行
return true;
}
注意:即使已经决定了重定向,还是需要return false;否则处理流程会继续向执行,控制器中的方法还是会被调用,达不到阻止运行的效果!
所有的拦截器都需要在Spring的配置文件中进行配置,在SpringMVC框架中,允许使用若干个拦截器,形成拦截器链,即某个请求可能需要经过多个拦截器,仅当每个拦截器都放行时,才会执行控制器中的方法!在配置文件中,配置的先后顺序决定了多个拦截器的执行顺序:
<mvc:interceptors>
<!-- 配置第1个拦截器 -->
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/main/index.do"/>
<!-- 拦截器类 -->
<bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>
<!-- 配置第2个拦截器 -->
</mvc:interceptors>
在配置每个拦截器时,允许使用若干个<mvc:mapping>
节点以配置若干个拦截路径,例如:
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/main/index.do"/>
<mvc:mapping path="/user/password.do"/>
<mvc:mapping path="/user/info.do"/>
<mvc:mapping path="/user/handle_password.do"/>
<mvc:mapping path="/user/handle_info.do"/>
<!-- 拦截器类 -->
<bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>
在配置路径,还可以使用*
作为通配符,例如:
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/main/*"/>
<!-- 拦截器类 -->
<bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>
在使用*
作为通配符时,需要注意,1个星号只能匹配1层路径,例如/main/*
可以匹配上/main/index.do
,也可以匹配/main/hello.do
,但是,不可以匹配上/main/a/index.do
!
如果一定要匹配若干层路径,必须使用2个星号,例如配置为/main/**
,可以匹配上/main/index.do
,也可以匹配/main/a/hello.do
,甚至可以匹配/main/a/b/c/d/hello.do
,即无视路径中后续的层级。
所以,可以配置为:
<mvc:interceptor>
<!-- 拦截路径 -->
<mvc:mapping path="/main/**"/>
<mvc:mapping path="/user/**"/>
<!-- 拦截器类 -->
<bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>
如果通配符匹配的路径过多,需要从中去除某些请求路径,还可以添加例外:
<mvc:interceptor>
<!-- 拦截路径:黑名单 -->
<mvc:mapping path="/main/**"/>
<mvc:mapping path="/user/**"/>
<!-- 例外路径:白名单 -->
<mvc:exclude-mapping path="/user/reg.do"/>
<mvc:exclude-mapping path="/user/handle_reg.do"/>
<mvc:exclude-mapping path="/user/login.do"/>
<mvc:exclude-mapping path="/user/handle_login.do"/>
<!-- 拦截器类 -->
<bean class="cn.tedu.spring.LoginInterceptor"></bean>
</mvc:interceptor>
也就是说,凡/user/
下的路径都会经过该拦截器,但是,/user/login.do
是不被处理的!添加到“例外”中的路径,在请求时,并不是拦截器直接放行,而是拦截器根本就不执行!
请求参数的乱码解决方案
<node>
<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>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</node>
在SpringMVC中统一处理异常
在Java中,异常的体系结构:
Throwable
Error
OutOfMemoryError
Exception
SQLException
IOException
FileNotFoundException
RuntimeException
NullPointerException
ClassCastException
NumberFormatException
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
StringIndexOutOfBoundsException
在Exception
中,除了RuntimeException
及其子孙类异常,其它的异常都是必须通过语法进行处理的!处理方式可以是try...catch
进行捕获并处理,也可以是在方法的签名中添加throws
声明抛出。
其实,处理异常的本质应该是:给予用户某些提示,告之操作失败的原因,避免用户再次提交错误的数据,另外,也可能在处理过程中,对已经发生的错误尽可能的补救,例如关闭某些已经打开的流对象。
某一种异常在项目的多个请求处理过程中,都可能出现,在SpringMVC中,提供了统一处理异常的做法:将@ExceptionHandler
添加在自定义的用于处理异常的方法之前,该自定义方法应该:
使用
public
权限;返回值的设计原则与处理请求的方法的相同;
方法名称可以自由定义;
方法中必须添加异常类型的参数,另外还可以添加例如
HttpServletRequest
等参数,但不可以添加其它参数。
例如:
@ExceptionHandler({IndexOutOfBoundsException.class, NullPointerException.class})
public String handleException(Throwable ex, HttpServletRequest request) {
String errorMessage = null;
if (ex instanceof NullPointerException) {
errorMessage = "错误:请提交用户名!";
} else if (ex instanceof IndexOutOfBoundsException) {
errorMessage = "错误:使用的索引超出了界限!";
}
request.setAttribute("msg", errorMessage);
return "error";
}
在@ExceptionHandler
注解中,可以配置需要处理的异常的种类,当配置后,仅当指定的异常出现时,才会调用匹配的方法进行处理,而其它异常是不予处理的!如果没有配置需要处理哪些异常,则任何异常出现都会进行处理!
在处理时,@ExceptionHandler
只能作用于当前控制器类!