Interceptor拦截器的基本使用方法
springMVC提供了Interceptor拦截器机制,类似于Servlet中的Filter过滤器,用于拦截用户请求并作出相应的处理。
通过拦截器机制可以进行用户权限鉴定,或者用来判断用户是否已经登录。
springMVC拦截器是可插拔式的设计,需要拦截器的某一个功能时,只需要在配置文件中应用该拦截器即可;如果不需要这个拦截器功能,只需要在配置文件中取消这个功能即可。
在SpringMVC中定义拦截器有两种办法:
- 实现HandlerInterceptor接口,或者继承实现HandlerInterceptor接口的实现类(例如HandlerInterceptorAdapter);
- 实现WebRequestInterceptor接口 ,或者继承WebRequestInterceptor接口的实现类。
1、实现HandlerInterceptor接口
handlerInterceptor接口位于org.springframework.web.servlet包下:
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
这三个方法的具体作用是:
- preHandle方法:该方法在执行Controller控制器方法之前执行。返回值为布尔值类型,如果返回为false,表示拦截该请求,不向下执行;如果返回值为true,则不拦截该请求,放行该请求,程序继续向下执行(如果后面没有其他的拦截器,那么就执行Controller控制器中的方法)。因为该方法可以对请求进行判断,决定是否继续执行,所以该方法常用来进行一些初始化操作或者对请求进行预处理;
- postHandle方法:该方法将在执行Controller控制器方法之后调用,但是在返回ModelAndView之前调用。在DispatcherServlet进行渲染之前调用,所以此方法常用于处理返回的视图,在此方法中对请求域中的视图和模型做进一步处理和修改;
- afterCompletion方法:该方法在Controller控制器方法执行完成后执行,由于该方法实在执行完成Controller方法之后执行,所以常用于一些资源清理、记录执行的日志等操作。
注意:由于preHandle方法决定了程序是否继续执行,因此postHandle方法和afterCompletion方法只有在preHandle方法返回值为true时才执行。
实现了handlerInterceptor接口后,只有在SpringMVC的配置文件中添加mvc:interceptors标签对才能起到全局拦截器的作用:
<!-- 配置拦截器-->
<mvc:interceptors>
<!-- 在mvc:interceptor标签中直接使用Bean标签会拦截所有请求-->
<bean class="com.springmvc.interceptor.MyInterceptor"/>
<!--定义多个拦截器,将会按顺序执行-->
<mvc:interceptor>
<!-- mvc:mapping path配置为/**,表示拦截所有请求-->
<mvc:mapping path="/**"/>
<!-- mvc:exclude-mapping path配置为/hello表示不会拦截/hello请求 -->
<mvc:exclude-mapping path="/hello"/>
<bean class="com.springmvc.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/hello"/>
<bean class="com.springmvc.interceptor.MyInterceptor2"/>
</mvc:interceptor>
...<!-- 可以接着配置更多的拦截器-->
</mvc:interceptors>
2、实现WebRequestInterceptor接口
WebRequestInterceptor接口中定义了三个方法,这三个方法都传递了同一个参数WebRequest.
WebRequest是spring定义的一个接口,在WebRequestInterceptor中对WebRequest的所有操作都将同步到HttpServletRequest中,然后在当前请求中一直传递。
public interface WebRequestInterceptor {
void preHandle(WebRequest var1) throws Exception;
void postHandle(WebRequest var1, ModelMap var2) throws Exception;
void afterCompletion(WebRequest var1, Exception var2) throws Exception;
}
WebRequestInterceptor接口的三个方法介绍如下:
- preHandle(WebRequest var1)方法:该方法将在Controller方法调用之前调用,这个方法与HandlerInterceptor接口的preHandle方法不同,WebRequestInterceptor的preHandle方法没有返回值,一般用来进行资源的准备工作,比如某些情况下需要提前准备一个session对象,我们可以利用WebRequest的setAttribute(name,value,scope)方法把session放到WebRequest中,setAttribute方法的第三个参数scope是Integer类型的,在WebRequest的父接口RequestAttributes中对它定义了三个常量:
1. SCOPE_REQUEST:它的值是0,表示只有在request中可以访问;
2. SCOPE_SESSION:它的值是1,如果环境允许的话,它代表的是一个局部隔离的session,否则就代表普通的session,并且可以在该session中可以访问;
3. SCOPE_GLOBAL_SESSION:它的值是2,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session范围内可以访问。 - postHandle(WebRequest var1, ModelMap var2)方法:该方法将在Controller方法被调用之后调用,但是在视图渲染之前调用。可以在这个方法中改变ModelMap数据模型来改变数据的展示。而WebRequest对象用于传递整个请求数据,在preHandle方法中准备的数据都可以在WebRequest中访问;
- afterCompletion(WebRequest var1, Exception var2)方法:该方法会在整个请求处理完成,也就是在试图返回并被渲染之后执行,因此该方法常用来进行资源的释放,而WebRequest可以把我们在preHandle方法中准备的资源传递到这里进行释放。Exception表示当前请求的异常对象,如果在Controller中捕获的异常已经被spring的异常处理器处理完成,那么这个异常对象为null。
Interceptor拦截器的执行流程
一、单个拦截器的执行流程
在程序运行时,拦截器的执行是有一定顺序的,该顺序与配置文件中的定义拦截器时的配置顺序有关。
如果配置文件中只定义了一个拦截器,那么该拦截器的执行流程如下:
程序首先执行拦截器类中的preHandle方法,如果该方法的返回值是true,则程序会继续向下执行处理器中的方法,否则不会再向下执行,在业务控制器Controller处理完请求后,会继续执行postHandle方法,而后会通过DispatcherServlet向客户端返回响应,在DispatcherServlet处理完请求后,才会继续执行afterCopmletion方法。
代码演示:
创建基于ssm的maven项目,名称叫做springmvc-interceptor的项目。配置好web.xml和applicationContext.xml文件。在包com.springmvc.controller中添加如下代码:
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello() {
System.out.println("HelloController的hello方法执行。");
return "hello";
}
}
hello.jsp页面为一个简单字符串展示:HelloWorld!.
在com.springmvc.interceptor包中创建MyInterceptor拦截器并实现HandleInterceptor接口,重写相关方法,代码如下:
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("MyInterceptor拦截器的preHandle方法执行。");
return true;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor拦截器的postHandle方法执行。");
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("MyInterceptor拦截器的afterCompletion方法执行。");
}
}
在springmvc.xml文件中配置拦截器:
<mvc:interceptors>
<mvc:interceptor>
<!-- mvc:mapping path配置为/**,表示拦截所有请求-->
<mvc:mapping path="/**"/>
<!-- mvc:exclude-mapping path配置为/hello22表示不会拦截/hello22请求 -->
<mvc:exclude-mapping path="/hello22"/>
<bean class="com.springmvc.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
将完全配置好的项目发布到tomcat中运行,访问http://localhost:8080/springmvc-interceptor/hello,页面进入hello.jsp页面。控制台输出如下:
从结果中可以看出,先执行拦截器的preHandle方法,然后执行了Controller的hello方法,再执行拦截器的postHandle方法,最后执行拦截器的afterCompletion方法。
二、多个拦截器的执行流程
当配置了多个拦截器时,会按照拦截器的配置顺序依次调用执行。但值得注意的是,拦截器的内部执行规律并步遵循普通的Java类一样,拦截器是基于“责任链”模式。
假如有两个拦截器,分别是MyInterceptor1和MyInterceptor2,将MyInterceptor1设置在前面,那么这两个拦截器的执行顺序如下图所示:
代码演示:
将上面演示用过的MyInterceptor拦截器复制两份,分别命名为MyInterceptor1和MyInterceptor2:
在springmvc.xml配置文件中定义这两个拦截器:
<!-- 配置拦截器-->
<mvc:interceptors>
<!--定义多个拦截器,将会按顺序执行-->
<mvc:interceptor>
<!-- mvc:mapping path配置为/**,表示拦截所有请求-->
<mvc:mapping path="/**"/>
<!-- mvc:exclude-mapping path配置为/h表示不会拦截/h请求 -->
<mvc:exclude-mapping path="/h"/>
<bean class="com.springmvc.interceptor.MyInterceptor1"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/h"/>
<bean class="com.springmvc.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
然后重新发布在tomcat中运行,访问http://localhost:8080/springmvc-interceptor/hello。浏览器跳转到hello.jsp页面,控制台输出:
由此可知,当多个拦截器同时工作时,它们的preHandle方法会按照配置文件中拦截器的配置顺序依次执行,如果返回值都为true,会戒指执行Controller控制器中的hello方法,而它们的postHandle方法和afterCompletion方法则会按照配置文件中拦截器的配置顺序反向执行。
使用Interceptor拦截器检验用户是否已经登录
通过以上的学习,我们可以利用拦截器的基本特性实现检验用户是否已经登录。如果当前没有登录,那么跳转到login.jsp登录页面,如果用户已经登录,则放行;如果账号或密码错误,则提示账号或密码错误并返回登录页面;已经登录用户退出时,页面自动返回登录页面。具体业务流程图如下:
在com.springmvc.controller包中创建UserController类:
@Controller
public class UserController {
@RequestMapping(value = "/login", method = RequestMethod.GET) //向用户登录页面的跳转方法
public String loginPage() {
System.out.println("用户从login的请求到登录跳转login.jsp页面");
return "login";
}
@RequestMapping(value = "/login", method = RequestMethod.POST) //用户实现登录的方法
public String login(User user, Model model, HttpSession session) {
String loginName = user.getLoginName();
String password = user.getPassword();
if (loginName != null && loginName.equals("zhangsan") && password != null && password.equals("123456")) {
System.out.println("用户登录功能实现");
//将用户添加至session中保存
session.setAttribute("CURRENT_USER", user);
return "redirect:index"; //重新定向到主页的index跳转方法
}
model.addAttribute("message", "账号或密码错误,请重新登录!");
return "login"; //跳转到登录页面
}
@RequestMapping(value = "/index") //向主页跳转的方法
public String indexPage() {
System.out.println("用户从index请求到主页跳转index.jsp页面");
return "index"; //跳转到主页面
}
@RequestMapping(value = "/logout") //用户退出登录的方法
public String logout(HttpSession session) {
session.invalidate(); //清除session
System.out.println("退出功能实现,清除session,重定向到login的请求");
return "redirect:login"; //重定向到登录页面的跳转方法
}
}
login.jsp的表单会提交loginName和password两个字符串,spring会自动把这两个字符串封装到User对象中。
在com.springmvc.interceptor包中创建LoginInterceptor的拦截器类:
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
String url=request.getRequestURI();//获取请求的URl
if (!(url.contains("Login")||url.contains("login"))) {
//非登录请求,获取session,判断是否有用户数据
if (request.getSession().getAttribute("CURRENT_USER")!=null) {
return true; //说明已经登录,放行
}else { //没有登录则跳转到登录页面,
request.setAttribute("message", "您还没有登录,请先登录!");
request.getRequestDispatcher("/login").forward(request, response);
}
}else {
return true; //登录请求,放行
}
return false; //默认拦截
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)throws Exception {
}
}
在preHandle方法中编写了控制用户登录的逻辑。首先判断是否去往登录页面,如果是则直接返回true放行,如果不是,则检测session中是否有user用户对象,如果没有则重定向到login界面。
在springmvc.xml配置文件中配置LoginInterceptor拦截器:
<mvc:interceptors>
<!-- 登录拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.springmvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
创建index.jsp页面:
创建login.jsp页面:
将项目发布到tomcat中运行。访问http://localhost:8080/springmvc-interceptor/index
从图中可以看出,用户没有登录就会被拦截器拦截到登录界面:
输入错误的账户密码会登录失败:
只有输入账户为zhangsan 密码为123456才会登录成功并跳转到index界面:
如果此时点击退出,就会清除session,然后跳转到登录页面。
如果我的文章对您有帮助,还请您多多支持我。支付宝帮忙扫一下吧