一、概要
Spring Web MVC的拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
二、核心接口
拦截器
preHandle方法
进入Handler方法之前执行。可以用于身份认证、身份授权。比如如果认证没有通过表示用户没有登陆,需要此方法拦截不再往下执行(return false),否则就放行(return true)。
postHandle方法
进入Handler方法之后,返回ModelAndView之前执行。可以看到该方法中有个modelAndView的形参。应用场景:从modelAndView出发:将公用的模型数据(比如菜单导航之类的)在这里传到视图,也可以在这里同一指定视图。
afterCompletion方法
执行Handler完成之后执行。应用场景:统一异常处理,统一日志处理等。
拦截器适配器
spring提供了一个HandlerInterceptorAdapter适配器(一种适配器设计模式的实现),允许我们只实现需要的回调方法
三、使用步骤
第一步 创建一个类实现 HandlerInterceptor
/**
* 现在需要实现用户登录检查——如果发现用户没有登录,跳转到登录页面,登
* 录成功后跳转回之前访问的页面。
*
* @author zhangwei
*/
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
public static final String USER_SESSION_NAME = "username";
/**
* 作用
* 预处理回调方法
* 1> 如果方法返回true 表示继续执行下一个拦截器
* 2> 如果方法返回false 请求处理流程中断
* 不会继续调用其他的拦截器或处理器方法,此时需要通过response产生响应
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object attribute = request.getSession().getAttribute(USER_SESSION_NAME);
log.debug("VerifyInterceptor====> preHandle方法执行" );
if (attribute != null) {
return true;
}
response.sendRedirect("/account/login?next=" + request.getRequestURI());
return false;
}
/**
* 后处理回调方法,
* 实现处理器的后处理(但在渲染视图之前),
* 此时可以通过modelAndView对模型数据进行处理或对视图进行处理
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
log.debug("VerifyInterceptor====> preHandle2方法执行" );
}
/**
* 整个请求处理完毕回调方法,即在视图渲染完毕时调用
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
log.debug("VerifyInterceptor====> afterCompletion 方法执行" );
}
}
第二步 控制器
@Controller
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/login")
public String login(HttpServletRequest request) {
request.getSession().setAttribute("username", "admin");
return "redirect:/";
}
}
四、注册方式
一种通过java类注册,另外一种通过xml注册
java类注册(spring-boot)
@Configuration
public class WebMvcConfigInterceptor implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加拦截器
registry.addInterceptor(new VerifyInterceptor())
// 添加拦截的url地址
.addPathPatterns("/**")
// 排除不拦截的url地址
.excludePathPatterns("/login");
}
}
xml注册
拦截所有的url
<mvc:interceptors>
<bean class="com.vip.mvc.interceptor.LoginInterceptor"/>
</mvc:interceptors>
拦截匹配的URL
<mvc:interceptors >
<mvc:interceptor>
<interceptor下面定义的mapping和exclude-mapping都是可以有多个的>
<!-- /user/* -->
<mvc:mapping path="/admin/**" />
<mvc:exclude-mapping path="/account/login" /><!-- 不拦截 -->
<bean class="com.vip.mvc.interceptor.LoginInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
注 拦截器语法
Wildcard | Description |
---|---|
? | 匹配任何单字符 |
* | 匹配0或者任意数量的字符 |
** | 匹配0或者更多的目录 |
栗子
匹配 | 说明 |
---|---|
/app/*.x | 匹配(Matches)所有在app路径下的.x文件 |
/app/p?ttern | 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern |
/**/example | 匹配(Matches) /app/example, /app/foo/example, 和 /example |
/app/**/dir/file. | 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java |
/*/.jsp | 匹配(Matches)任何的.jsp 文件 |
五、栗子
需求
- 用户请求url
- 拦截器进行拦截校验
- 如果请求的url是公开地址(无需登陆即可访问的url)。
- 如果用户session不存在,则跳转到登陆页面。
- 如果用户session存在,跳转相应的界面。
创建登录拦截器
/**
* 现在需要实现用户登录检查——如果发现用户没有登录,跳转到登录页面,登
* 录成功后跳转回之前访问的页面。
*
* @author zhangwei
*/
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
public static final String USER_SESSION_NAME = "username";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object attribute = request.getSession().getAttribute(USER_SESSION_NAME);
log.debug("VerifyInterceptor====> preHandle方法执行" );
if (attribute != null) {
return true;
}
response.sendRedirect("/account/login?next=" + request.getRequestURI());
return false;
}
}
创建登录控制器
@Controller
@RequestMapping("/account")
public class AccountController {
@RequestMapping("/login")
public String login(HttpServletRequest request) {
request.getSession().setAttribute("name", "admin");
// 重定向
return "redirect:/";
}
}
注册拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/"/>
<mvc:exclude-mapping path="/account/login"/>
<bean class="com.vip.mvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
性能检测案例
@Component
@Slf4j
public class PerformanceInterceptor extends HandlerInterceptorAdapter {
ThreadLocal<Long> threadLocal = new ThreadLocal<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//1、开始时间
long beginTime = System.currentTimeMillis();
threadLocal.set(beginTime);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//2、结束时间
long endTime = System.currentTimeMillis();
//3、消耗的时间
long consumeTime = endTime - threadLocal.get();
// 此处认为处理时间超过300毫秒的请求为慢请求
if (consumeTime > 300) {
log.error("请求url:{} 执行时间:{}" , request.getRequestURI(),consumeTime);
}
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.vip.mvc.interceptor.PerformanceInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
六、区别
1、过滤器:
依赖于servlet容器;
- 在实现上基于函数回调,可以对几乎所有请求进行过滤;
- 缺点是一个过滤器实例只能在容器初始化时调用一次;
- 使用过滤器的目的是用来做一些过滤操作,获取我们想要获取的数据,比如:在过滤器中修改字符编码;在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
2、拦截器:
- 依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架;
- 在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用;
- 缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理;
- 由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。