我一直以为Spring的拦截器是基于AOP原理实现的,仔细阅读完源码,才知道自己的认知是错误的。
今天我们就从源码的角度剖析下Spring拦截器。
一.创建一个自己的拦截器
实现一个自己的拦截器,需要实现HandlerInterceptor接口。接口内的三个核心方法如下:
1.preHandle():该方法在业务处理器处理请求之前调用。
2.postHandle():这个方法在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView 对象进行操作。
3.afterCompletion():在postHandle执行之后执行,发生异常也会执行。该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
我们创建一个用户登录的拦截器:
@Component
public class MyInterceptor implements HandlerInterceptor {
//在执行Controller方法之前来执行的
//用于用户认证校验、用户权限校验
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//得到请求的url
String url = request.getRequestURI();
//判断是否是公开地址
//实际开发中需要公开地址配置在配置文件中
if(url.indexOf("login.action")>=0){
//如果是公开地址则放行
return true;
}
//判断用户身份在session中是否存在
HttpSession session = request.getSession();
String usercode = (String) session.getAttribute("usercode");
//如果用户身份在session中存在放行
if(usercode!=null){
return true;
}
//执行到这里拦截,跳转到登陆页面,用户进行身份认证
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
//如果返回false表示拦截不继续执行handler,如果返回true表示放行
return false;
}
//在执行Controller方法之后返回modelAndView之前来执行
//如果需要向页面提供一些公用的数据或配置一些视图信息,使用此方法实现从modelAndView入手
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor...postHandle");
}
//完成对页面的渲染之后执行此方法
//作系统统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长
//实现系统统一日志记录
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("HandlerInterceptor...afterCompletion");
}
}
复制代码
二.拦截器的执行机制
我们知道SpringMVC框架的核心类为DispatchServlet,http请求的核心执行方法为
doService(),我们画下SpringMvc的工作流程图:
我们从流程图可以看到,拦截器的执行是在穿插在SpringMvc的工作流程中的,并没有用到动态代理机制,直接调用的拦截器方法。
拦截器在实现层面,并没有用到aop,并没有切面,通知这一类的代码,所以它的实现并不是基于aop的。但是拦截器从思想层面上,是面向切面编程的,是在controller这个层面上进行的代码织入。
三.拦截器如何添加到拦截器列表的
需要我们自己创建一个config,将拦截器添加进去。
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
//注册TestInterceptor拦截器
InterceptorRegistration registration = registry.addInterceptor(new MyInterceptor());
registration.addPathPatterns("/**"); //所有路径都被拦截
registration.excludePathPatterns( //添加不拦截路径
"你的登陆路径", //登录
"/**/*.html", //html静态资源
"/**/*.js", //js静态资源
"/**/*.css", //css静态资源
"/**/*.woff",
"/**/*.ttf"
);
}
}
复制代码
在类WebMvcConfigurationSupport中会创建RequestMappingHandlerMapping
在创建RequestMappingHandlerMapping时,会将自定义的拦截器加入到拦截器列表中。