一、拦截器
1、拦截器的介绍
Spring MVC 的拦截器(Interceptor)与Java Servlet的过滤器(Filter)类似,它主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、记录请求信息的日志、判断用户是否登录等功能上。在 Spring MVC 框架中定义一个拦截器需要对拦截器进行定义和配置,定义一个拦截器可以通过两种方式:一种是通过实现 HandlerInterceptor 接口或继承 HandlerInterceptor 接口的实现类来定义;另一种是通过实现 WebRequestInterceptor 接口或继承 WebRequestInterceptor 接口的实现类来定义。本文以第二种方式进行演示。
在HandlerInterceptor有三个方法,其描述如下:
- preHandle 方法:该方法在控制器的处理请求方法前执行,其返回值表示是否中断后续操作,返回 true 表示继续向下执行,返回 false 表示中断后续操作。
- postHandle 方法:该方法在控制器的处理请求方法调用之后、解析视图之前执行,可以通过此方法对请求域中的模型和视图做进一步的修改。
- afterCompletion 方法:该方法在控制器的处理请求方法执行完成后执行,即视图渲染结束后执行,可以通过此方法实现一些资源清理、记录日志信息等工作。
拦截器和过滤器的区别如下表:
过滤器(Filter) | 拦截器(Interceptor) | |
---|---|---|
使用范围不同 | Servlet规范规定的,只能用于Web程序中 | 既可用于Web程序,又可用于Applicaiont、Swing程序中 |
规范不同 | Servlet规范的补一份,任何Web工程都可以使用 | Spring 框架自己的,只有使用了Spring MVC框架的工程才可以使用 |
使用的资源不同 | 过滤器不行 | 拦截器是Spring的组件,归Spring管理,配置在Spring文件中,能使用Spring里的任何资源、对象,如:Service对象、数据源、事务管理等 |
深度不同 | 过滤器只在Servlet前后起作用。 | 拦截器能够深入到方法前后、异常抛出前后等,具有更大的弹性。 |
拦截的资源不同 | 在url-pattern中配置了/*之后,过滤器会对所有要访问的资源拦截 | 拦截器只拦截访问的控制器方法,如果访问的是jsp、html、css、image、js等,则不会拦截 |
2、单个拦截器的执行流程
在配置文件中如果只定义了一个拦截器,程序将首先执行拦截器类中的 preHandle 方法,如果该方法返回 true,程序将继续执行控制器中处理请求的方法,否则中断执行。如果 preHandle 方法返回 true,并且控制器中处理请求的方法执行后、返回视图前将执行 postHandle 方法,返回视图后才执行 afterCompletion 方法。
(1)配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!-- 指定Spring MVC配置文件的位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- encoding:指定一个具体的字符集 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- forceEncoding:处理 response中的字符编码 -->
<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>
</web-app>
(2)创建控制器类
@Controller
public class InterceptorController {
@RequestMapping("/testInterceptor")
public String testInterceptor(){
System.out.println("我是testInterceptor方法");
return "interceptorPage";
}
}
(3)创建拦截器类
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("我是preHandle方法---在控制器的处理请求方法调用之后,解析视图之前执行");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("我是postHandle方法---在控制器的处理请求方法调用之后,解析视图之前执行");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("我是afterCompletion方法---在控制器的处理请求方法执行完成后执行,即视图渲染结束之后执行");
}
}
(4)在springmvc.xml中配置拦截器类
<!-- 配置拦截器 -->
<mvc:interceptors>
<!-- 配置一个全局拦截器,拦截所有请求 -->
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<!-- 配置不需要拦截作用的路径 -->
<!-- <mvc:exclude-mapping path="" />-->
<!-- 定义<mvc:interceptor>元素中,表示匹配指定路径的请求才进行拦截 -->
<bean class="com.yht.example1.interceptor.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
在上述示例代码中,<mvc:interceptors> 元素用于配置一组拦截器,其子元素 <bean> 定义的是全局拦截器,即拦截所有的请求。
<mvc:interceptor> 元素中定义的是指定路径的拦截器,其子元素 <mvc:mapping> 用于配置拦截器作用的路径,该路径在其属性 path 中定义。
如上述示例代码中,path 的属性值“/**”表示拦截所有路径,“/**”表示拦截所有的路径。如果在请求路径中包含不需要拦截的内容,可以通过 <mvc:exclude-mapping> 子元素进行配置。
需要注意的是,<mvc:interceptor> 元素的子元素必须按照 <mvc:mapping.../>、<mvc:exclude-mapping.../>、<bean.../> 的顺序配置。
(5)创建interceptorPage.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
视图
<%System.out.println("视图渲染结束。"); %>
</body>
</html>
(6)启动 Tomcat 服务器,然后通过地址“http://localhost:8080/SpringMVCStudy02_war/testInterceptor”测试拦截器。输出结果如下:
3、多个拦截器的执行流程
在 Web 应用中通常需要有多个拦截器同时工作,这时它们的 preHandle 方法将按照配置文件中拦截器的配置顺序执行,而它们的 postHandle 方法和 afterCompletion 方法则按照配置顺序的反序执行。如下图:
(1)创建拦截器类TestInterceptor。
public class TestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("我是preHandle方法---TestInterceptor类");
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("我是postHandle方法---TestInterceptor类");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("我是afterCompletion方法---TestInterceptor类");
}
}
(2)在springmvc.xml中配置TestInterceptor。
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<bean class="com.yht.example1.interceptor.MyInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<!-- 配置拦截器作用的路径 -->
<mvc:mapping path="/**" />
<bean class="com.yht.example1.interceptor.TestInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
(3)启动 Tomcat 服务器,然后通过地址“http://localhost:8080/SpringMVCStudy02_war/testInterceptor”测试拦截器。输出结果如下:
注意:
在preHandle中如果返回false,则表示不放行,那么此时我们可以通过request.getRequestDispatcher("/WEB-INF/pages/页面名").forward(request,response);跳转至某页面。
二、异常处理
在 Spring MVC 应用的开发中,不管是对底层数据库操作,还是业务层或控制层操作,都会不可避免地遇到各种可预知的、不可预知的异常需要处理。HandlerExceptionResolver 接口用于解析请求处理过程中所产生的异常。
(1)自定义异常类SystemException。
public class SystemException extends Exception{
private String message;
public SystemException(String message) {
this.message = message;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(2)在pages下创建error.jsp。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h4>这是错误页面</h4>
${msg}
</body>
</html>
(3)创建一个 HandlerExceptionResolver 接口的实现类SystemExceptionHandler
public class SystemExceptionHandler implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
SystemException systemException = null;
if(e instanceof SystemException){
systemException = (SystemException) e;
}else{
e = new SystemException("系统异常");
}
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
mv.addObject("msg", e.getMessage());
return mv;
}
}
(4)创建控制器
@Controller
public class ExceptionController {
@RequestMapping("/testException")
public String testException() throws Exception{
System.out.println("我是testException方法");
int num = Integer.parseInt("a123");
return "success";
}
}
(5)在springmvc.xml中配置SystemExceptionHandler
<bean class="com.yht.example1.handler.SystemExceptionHandler"></bean>
(6)启动 Tomcat 服务器,然后通过地址“http://localhost:8080/SpringMVCStudy02_war/testException”测试拦截器。界面结果如下: