看过之前的蜕变系列文章,相信你对springMVC有了一定的认识。对springMVC的文件上传,也有了一定的认识。今天我们来开启新讨论,讲一讲springMVC的Interceptor拦截器怎么去处理web层面通用逻辑。
每月底工厂君会根据后台记录筛选转发文章前三位的朋友,给与奖励,第一名100元,第二名50元,第三名30元的现金奖励。行动力超强的你,关注公号,转发文章,赶紧动起来吧!
猿蜕变同样是一个原创系列文章,帮助你从一个普通的小白,开始掌握一些行业内通用的框架技术知识以及锻炼你对系统设计能力的提升,完成属于你的蜕变,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取!
在web开发中,我们有很多需要统一去处理的事情,我们先回忆下之前解决过的一个问题——请求的中文乱码问题。为了处理中文的乱码问题,我们编写并配置了过滤器(Filter),统一对字符做了UTF-8编码。
其实在程序中,还有很多类似需要全局统一处理的事情要做,比如登录验证、权限验证、请求访问时长统计,web应用层关键日志输出 ……为了解决这些需要去统一处理的问题,Spring MVC提供了拦截器(Interceptor)来处理。Spring MVC的拦截器有框架提供的,也提供了接口让用户自己去定义。用户自定义拦截器要求实现接口org.springframework.web.servlet.HandlerInterceptor。我们看下接口的定义(去掉注释的):
package org.springframework.web.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.method.HandlerMethod;
public interface HandlerInterceptor {
boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)
throws Exception;
void postHandle(
HttpServletRequest request,HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
void afterCompletion(
HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex)
throws Exception;
}
接口有三个方法需要实现:
boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler)
throws Exception;
返回值为 boolean类型,如果返回为 true,就会执行controller方法,并且会将afterCompletion()方法放到一个方法栈中等待执行。大家注意方法入参,request和response都存在,可以做的事情很多了。
void postHandle(
HttpServletRequest request,HttpServletResponse response, Object handler, ModelAndView modelAndView)
throws Exception;
无返回值类型,这个方法会在Controller方法执行完成之后执行。如果Controller的方法没有被执行到,那么这个方法也不会执行。大家注意一下,方法有个入参ModelAndView 类型的,这意味着我们可以修改返回视图。
void afterCompletion(
HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex)
throws Exception;
当preHandle返回为true时,这个方法会被放到一个方法栈等待执行,什么时候执行呢?等待视图渲染完成之后再执行。大家注意方法参数有Exception类型的,在这里可以处理异常。
看看DispatcherServlet的源码的doDispatch方法就知道了,接下来我们自己实现一个拦截器:
package com.pz.web.study.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class FirstInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponseresponse, Object handler) throws Exception{
System.out.println("==preHandle==被执行了");
return true;
}
@Override
publicvoidpostHandle(HttpServletRequest request,
HttpServletResponseresponse, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("==postHandle==被执行了");
}
@Override
publicvoidafterCompletion(HttpServletRequest request,
HttpServletResponseresponse, Object handler, Exception ex)
throws Exception {
System.out.println("==afterCompletion==被执行了");
}
}
为了更好的查看效果我们编写个新的Controller
package com.pz.web.study.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class InterceptorControllerDemo {
@RequestMapping("/InterceptorControllerDemo.do")
public String InterceptorControllerDemo() throws Exception {
System.out.println("Interceptor方法==InterceptorControllerDemo===被执行了");
return"../index";
}
}
增加拦截器配置,修改spring-servlet.xml增加内容
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.FirstInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
启动应用,访问
http://127.0.0.1/InterceptorControllerDemo.do
注意控制台输出:
=FirstInterceptor=preHandle==被执行了
=Controller方法==InterceptorControllerDemo===被执行了
=FirstInterceptor=postHandle==被执行了
=FirstInterceptor=afterCompletion==被执行了
我们观察配置文件,mvc:interceptors发现拦截器有多个,那么有多个拦截器时怎么执行呢?哪个拦截器先执行?
为了搞明白这个我们再定义个拦截器吧:
package com.pz.web.study.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class SecondInterceptor implements HandlerInterceptor{
@Override
public booleanpreHandle(HttpServletRequest request,
HttpServletResponseresponse, Object handler) throws Exception {
System.out.println("=SecondInterceptor=preHandle==被执行了");
return true;
}
@Override
public void postHandle(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler,
ModelAndView modelAndView)throws Exception {
System.out.println("=SecondInterceptor=postHandle==被执行了");
}
@Override
public voidafterCompletion(HttpServletRequest request,
HttpServletResponseresponse, Object handler, Exception ex)
throws Exception {
System.out.println("=SecondInterceptor=afterCompletion==被执行了");
}
}
修改拦截器配置
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.FirstInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
启动应用继续访问:
http://127.0.0.1/InterceptorControllerDemo.do
控制台输出
=FirstInterceptor=preHandle==被执行了
=SecondInterceptor=preHandle==被执行了
=Controller方法==InterceptorControllerDemo===被执行了
=SecondInterceptor=postHandle==被执行了
=FirstInterceptor=postHandle==被执行了
=SecondInterceptor=afterCompletion==被执行了
=FirstInterceptor=afterCompletion==被执行了
preHandle方法是按照配置顺序执行的,而postHandle和afterCompletion方法是按照配置逆序执行的!
我们在生活中经常遇到下面的场景,我们访问某些站点,一些功能需要在登录后才能使用。下面我们就来实现这样一个小功能:让url为/user/下访问的 Contoller方法,只能使用用户名为pangzi的用户才能访问。我们看下面这个例子:
先编写用于登录的页面login.jsp:
<%@ page language="java" contentType="text/html;charset=utf-8"
pageEncoding="utf-8"isELIgnored="false"%>
<html>
<head>
<title>spring form表单样例使用对象接收参数</title>
</head>
<body>
<form action="./login.do"method=post>
<lable>用户名:</lable>
<input type="text"name="userName" id="userName"/><br />
<lable>密码:</lable>
<input type="password"name="passWord" id="phone"/><br />
<input type="submit"value="登录"id="submit" /><br />
</form>
</body>
</html>
编写用于登录的Controller:
package com.pz.web.study.springmvc.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import com.pz.web.study.springmvc.domain.User;
@Controller
public class LoginControllerDemo {
@RequestMapping("/login.do")
public String userAction(String userName,StringpassWord,HttpServletRequest request) throws Exception {
if(userName.equals("pangzi")&&passWord.equals("123456")){
User user= new User();
user.setUserName(userName);
request.getSession().setAttribute("user", user);
return"../index";
}
return"../login";
}
}
编写权限访问控制拦截器
package com.pz.web.study.springmvc.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.pz.web.study.springmvc.domain.User;
public class UserLoginInterceptor implements HandlerInterceptor{
@Override
public booleanpreHandle(HttpServletRequest request,
HttpServletResponseresponse, Object handler) throws Exception {
String uri=request.getRequestURI();
if(uri.contains("/user/")){
User user = (User)request.getSession().getAttribute("user");
if(null==user){
response.sendRedirect("/login.jsp");
}
}
System.out.println("=UserLoginInterceptor=preHandle==被执行了");
return true;
}
@Override
public void postHandle(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler,
ModelAndView modelAndView)throws Exception {
System.out.println("=UserLoginInterceptor=postHandle==被执行了");
}
@Override
public void afterCompletion(HttpServletRequestrequest,
HttpServletResponseresponse, Object handler, Exception ex)
throws Exception {
System.out.println("=UserLoginInterceptor=afterCompletion==被执行了");
}
}
修改spring-servlet.xml增加拦截器配置:
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.FirstInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.SecondInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.pz.web.study.springmvc.interceptor.UserLoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
编写一个用于验证的Controller:
package com.pz.web.study.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/user")
publicclass UserLoginControllerDemo {
@RequestMapping("/userAction.do")
public String userAction() throws Exception {
System.out.println("userAction成功执行了");
return"../index";
}
}
启动应用,访问:
http://127.0.0.1/user/userAction.do
页面跳转到登录页面,输入
用户名 pangzi
密码 123456
登录后
然后再访问
http://127.0.0.1/user/userAction.do
看看效果
我建了一个群,群里有很多高手,欢迎大家入群探讨。