编程|拦截器和过滤器

拦截器 【编程|拦截器和过滤器】对action请求起作用,动态拦截action调用的对象。主要是拦截客户请求并作出相应的处理。
实现原理
基于Java反射机制(动态代理)来实现的。
不依赖servlet容器
触发时机:请求进入servlet后,在进入Controller之前
编程|拦截器和过滤器
文章图片

拦截器的方法

方法名
说明
preHandle()
此方法将在请求处理之前调用,返回值是Boolean类型。如果返回false,标识请求结束,之后的拦截器和controller都不会执行;如果返回true,则会继续调用下一个拦截器的preHandle()方法。
postHandle()
该方法是在请求处理之后被调用,前提是preHandle()方法的返回值全部为true才能被调用,并且会在Dispatcher Servlet进行视图返回 渲染之前被调用,所以可以在这个方法中对controller处理之后的ModelAndView对象进行操作。
afterCompletion()
该方法是在整个请求结束之后,即在Dispatcher Servlet渲染了对应的视图之后执行,前提是preHandle()方法的返回值全部为true才能被调用。
执行流程图
编程|拦截器和过滤器
文章图片

1.根据当前请求,找到HandlerExecutionChain执行链,执行链可以处理当前请求的handler方法以及handler方法的所有拦截器;
2.按顺序执行所有拦截器的preHandle方法;
(1)如果当前拦截器preHandle返回为true,说明放行,则执行下一个拦截器的preHandle方法(2)如果当前拦截器preHandle返回为false,说明不放行,则倒序执行所有已经执行了的拦截器的afterCompletion方法(不执行当前返回false拦截器的afterCompletion方法);
3.所有拦截器都返回true才会执行目标方法;
4.倒序执行所有拦截器的postHandle方法;
5.之前的所有步骤如果有任何异常就不会继续向下执行,但都会直接倒序触发afterCompletion;
6.如何没有任何异常,页面成功渲染完成以后,也会倒序触发afterCompletion方法。

使用拦截器(多个拦截器)
1.创建拦截器类实现HandlerInterceptor接口
(两个拦截器)
public class OneInterceptor implements HandlerInterceptor {// 处理请求之前执行,controller处理之前 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) { String url =String.valueOf(request.getRequestURL()) ; System.out.println("1、url=="+url); // 放开拦截 return true; }// 处理请求之后执行,(controller处理之后,DispatcherServlet进行视图渲染之前) @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) { System.out.println("1、postHandle"); }// 响应之后执行(DispatcherServlet进行视图渲染之后,返回前端) @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) { System.out.println("1、afterCompletion"); } }

public class TwoInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception { String url =String.valueOf(request.getRequestURL()) ; System.out.println("2、url=="+url); // 放开拦截 return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { System.out.println("2、postHandle"); } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { System.out.println("2、afterCompletion"); } }

2.注入拦截器
@Configuration public class WebMvcConfig implements WebMvcConfigurer {@Override public void addInterceptors(InterceptorRegistry registry) { // 拦截所有路径 // 注册自定义两个拦截器 registry.addInterceptor(new OneInterceptor()).addPathPatterns("/**"); registry.addInterceptor(new TwoInterceptor()).addPathPatterns("/**"); }}

3.测试,访问路径
@RequestMapping("/reqUrl") public void reqUrl () { System.out.println("目标方法执行...."); }


编程|拦截器和过滤器
文章图片
Filter过滤器 实现原理
基于函数回调
依赖servlet容器,在进入tomcat容器之后,进入servlet之前执行
方法
init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。
doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
destroy(): 当容器销毁过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次
public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException {System.out.println("Filter 前置"); }@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {System.out.println("Filter 处理中"); filterChain.doFilter(servletRequest, servletResponse); }@Override public void destroy() {System.out.println("Filter 后置"); } }

使用
Filter流程图:
用户登录
编程|拦截器和过滤器
文章图片

@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpSession session = httpServletRequest.getSession(); Object user = session.getAttribute("user"); // 如果 user = null; 说明 user 还没有登陆 if (user == null) { httpServletRequest.getRequestDispatcher("/xxxx").forward(httpServletRequest,servletResponse); return; } else { // 让程序继续往下访问用户的目标资源 filterChain.doFilter(servletRequest,servletResponse); } }

FilterChain过滤器链
执行流程:
编程|拦截器和过滤器
文章图片

多个Filter执行顺序

1.在web.xml中,filter执行顺序跟的顺序有关,先声明的先执行
2.使用注解配置的话,filter的执行顺序跟名称的字母顺序有关,例如AFilter会比BFilter先执行
3.如果既有在web.xml中声明的Filter,也有通过注解配置的Filter,那么会优先执行web.xml中配置的Filter

    推荐阅读