1、HandlerInterceptor 接口
第一个方法:preHandle() 预先处理器:在我们的目标方法处理之前,进行处理
第二个方法:postHandle() 目标方法执行完之后,还没到达页面之前执行,我们有可能会在页面上再放一些数据,
第三个方法afterCompletion():页面渲染完之后,我们可以用这个方法做一些清理工作
现在我们可以写一个拦截器用于我们页面的登录检查功能
1.我们想要定制化配置,首先写一个配置类 AdminConfig 让它实现WebMvcConfig接口
2.重写里面的addInterceptors(InterceptorRegistry registry)方法
3.这个方法需要添加一个拦截器 registry.addInterceptor(new LoginInterceptor())
4.我们需要自定义一个拦截器 LoginInterceptor
5.然后写他的拦截逻辑
@Configuration
public class AdminConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")//拦截所有请求,包括静态资源
//这边我们可以配置静态资源路径,但是这种方法在这里很麻烦。放行指定路径
.excludePathPatterns("/","/login","/css/**","/js/**","/fonts/**","/images/**");
}
};
}
}
=============================================================================
* @create 2022-08-16 9:44
* 登陆检查
* 我们登录之后,访问的每一个请求都应该是登录之后才可以访问的
* 我们开始写的只做了index页面的检查,如果每一个页面都这样写就会显得代码冗余,还特别麻烦
* 所以我们可以使用拦截器
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
//目标方法之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("拦截的请求路径是:" + requestURI);
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住,说明没登录,让它跳转到登录界面,并提示请先登录
request.setAttribute("msg","请先登录");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
//目标方法之后
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
//页面渲染之后
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
2. 拦截器原理
1.根据请求获得 获得HandlerExecutionChain【可以处理请求的handler以及handler的所有的拦截器 】
2.找到这个handler的适配器,用来执行目标方法的
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
3.在执行目标方法之前,先执行我们的拦截器的prehandle()方法:
1.先来顺序执行所有的拦截器方法 【prehandle()方法】
- 如果这个当前拦截器的preHandle()方法执行成功,返回的为true,就执行下一个拦截器的preHandle()
- 如果返回的是false,会直接触发triggerAfterCompletion()方法,这个方法,他先会记录下当前拦截器的索引,然后倒序执行已经执行了的afterCompletion()方法。
2.接着执行下一个拦截器,直到所有的拦截器执行完,中间如果有任何一个拦截器返回false,直接跳出不执行目标方法。
3.只有所有拦截器全返回true,执行目标方法
4.执行完目标方法之后,调用applyPostHandle(),倒序执行postHandle()方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
5.前面的步骤有任何异常,都会直接出发triggerAfterCompletion()调用afterCompletion()
6.如果一切正常来到页面渲染逻辑
7.页面成功渲染完成以后,也会倒序出发afterCompletion()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
applyPreHandle()
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
//遍历我们的拦截器
HandlerInterceptor interceptor = this.interceptorList.get(i);
//执行拦截器的preHandle方法
//如果第一个拦截器调用完返回的是false,表示已经拦截了,接着会调用
//triggerAfterCompletion(request, response, null)这个方法
if (!interceptor.preHandle(request, response, this.handler)) {
//这个方法,会倒序执行,每一个拦截器的afterCompletion()
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
======
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}