目录
原理大概:
核心方法是DispatcherServerlet中的doDispatcher():
//1.
mapperHandler=getHandler(processedRequest);
//2.
HandlerAdapter ha=getHandlerAdapter(mapperedHandler.getHandler());
首先通过getHandler()方法,找到handler(也就是controller下对应的方法),并且会返回一个HandlerExcutionChain执行链,HandlerExcutionChain中有handler以及拦截器interceptor的信息;
然后我们通过getHandlerAdapter()方法找到合适的处理器适配器;
在执行handle()方法之前我们先执行HandlerExcutionChain中的拦截器:
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
按顺序执行所有拦截器的preHandle()方法,如果拦截器preHandle()方法返回为true,则执行下一个拦截器的preHandle,若为false,则倒序执行已经执行了的拦截器的postHandle()方法,若出现异常则触发afterCompletion;
最后处理适配器,执行handler,返回ModelAndView:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
自定义拦截器-SSM
<!--配置拦截器-->
<mvc:interceptors>
<bean class="com.wyh.web.HandlerInterceptor1"></bean>
<bean class="com.wyh.web.HandlerInterceptor2"></bean>
</mvc:interceptors>
具体:
<mvc:interceptors>
<!--拦截所有-->
<bean class="com.wyh.web.Interceptor1"></bean>
<mvc:interceptor>
<!--拦截路径-->
<mvc:mapping path="/users"/>
<bean class="com.wyh.web.Interceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
eg:
package com.yanzhen.utils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class PathInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
request.setAttribute("basePath",basePath);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<bean class="com.yanzhen.utils.PathInterceptor" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<mvc:exclude-mapping path="/login"/>
<mvc:exclude-mapping path="/logout"/>
<!-- 这里是/captcha/*是因为后面只有一个子路径了-->
<mvc:exclude-mapping path="/captcha/*"/>
<!-- 后面还有多个文件-->
<mvc:exclude-mapping path="/static/**"/>
<bean class="com.yanzhen.utils.PermissionInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
自定义拦截器—springboot
首先需要明确自定义的拦截器需要放到容器中的:
1.首先自定义一个config类,实现WebMvcConfigurer;
然后重写里面的方法,将自定义的拦截器注册到容器中,并且实现addPathPatterns("/**"),所 有请求被拦截,然后指定放行掉的路径资源 .excludePathPattens("/xxx/xxx");
package com.atguigu.admin.config;
/**
* 1、编写一个拦截器实现HandlerInterceptor接口
* 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
* 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
*
* @EnableWebMvc:全面接管
* 1、静态资源?视图解析器?欢迎页.....全部失效
*/
//@EnableWebMvc
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**") //所有请求都被拦截包括静态资源
.excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**",
"/js/**","/aa/**"); //放行的请求
}
2.自定义拦截器:实现HandlerInterceptor接口,重写preHandle()、PostHandle()、 afterCompletion() ;
package com.atguigu.admin.interceptor;
/**
* 登录检查
* 1、配置好拦截器要拦截哪些请求
* 2、把这些配置放在容器中
*/
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
/**
* 目标方法执行之前
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String requestURI = request.getRequestURI();
log.info("preHandle拦截的请求路径是{}",requestURI);
//登录检查逻辑
HttpSession session = request.getSession();
Object loginUser = session.getAttribute("loginUser");
if(loginUser != null){
//放行
return true;
}
//拦截住。未登录。跳转到登录页,不用response.setAttribute()
// resp.sendRedirect()不能显示提示信息msg的原因就是:重定向已经是服务器重新响应到登录页面上,此时没有操作,固然不会显示msg
// 而请求转发,是请求域中放了msg,所以说前端可以取到请求域中的内容
request.setAttribute("msg","请先登录");
// re.sendRedirect("/");
request.getRequestDispatcher("/").forward(request,response);
return false;
}
/**
* 目标方法执行完成以后
* @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.info("postHandle执行{}",modelAndView);
}
/**
* 页面渲染以后
* @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.info("afterCompletion执行异常{}",ex);
}
}
注意:如果任何一个拦截器返回false,直接跳出不执行该目标方法,且倒序执行返回为true的拦截器的postHandle()方法(作用于方法执行完后与视图层阶段),如果出现异常就会触发afterCompletion()方法,就算页面成功渲染也一样会倒序触发这个方法;