拦截器介绍

Java中的拦截器是动态拦截 action 调用的对象,然后提供了可以在 action 执行前后增加一些操作,也可以在 action执行前停止操作,功能与过滤器类似,但是标准和实现方式不同。

  • 登录认证:在一些应用中,可能会通过拦截器来验证用户的登录状态,如果没有登录或者登录失败,就会给用户一个友好的提示或者返回登录页面,当然大型项目中都不采用这种方式,都是调单点登录系统接口来验证用户。
  • 记录系统日志:我们在常见应用中,通常要记录用户的请求信息,比如请求 ip,方法执行时间等,通过这些记录可以监控系统的状况,以便于对系统进行信息监控、信息统计、计算 PV、性能调优等。
  • 通用处理:在应用程序中可能存在所有方法都要返回的信息,这是可以利用拦截器来实现,省去每个方法冗余重复的代码实现。

使用拦截器

我们需要实现 HandlerInterceptor 类,并且重写三个方法

  • preHandle:在 Controoler 处理请求之前被调用,返回值是 boolean类型,如果是true就进行下一步操作;若返回false,则证明不符合拦截条件,在失败的时候不会包含任何响应,此时需要调用对应的response返回对应响应。
  • postHandler:在 Controoler 处理请求执行完成后、生成视图前执行,可以通过ModelAndView对视图进行处理,当然ModelAndView也可以设置为 null。
  • afterCompletion:在 DispatcherServlet 完全处理请求后被调用,通常用于记录消耗时间,也可以对一些资源进行处理。

1. 定义login的拦截器

public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 进入controller方法之前
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        System.out.println("LoginInterceptor ==》进入preHandle");
        //进入controller之前验证token的值
        String token = request.getParameter("token");
        System.out.println(request.getRequestURL());

        //验证用户登录
        Object user = request.getSession().getAttribute("user");
        if (user != null) {
            return true;
        } else {
            System.out.println("用户未登录...");
            // request.getRequestDispatcher("/index.html").forward(request, response);
            // response.sendRedirect(request.getContextPath()+"login.html");
            response.sendRedirect("/login.html");

            return false;
        }
   /*     response.getWriter().print("失败!");
        return HandlerInterceptor.super.preHandle(request, response, handler);
    */
    }

    /**
     * 调用完controller,视图渲染之前
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {


        System.out.println("LoginInterceptor ==》postHandle");
        HandlerInterceptor.super.postHandle(request, response, handler, 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 {

        System.out.println("LoginInterceptor ==》afterCompletion");

        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2. 注册刚才加的LoginInterceptor,并制定拦截的路径

注解拦截器注意的地方。要么是类上面没加注解,要么就是拦截的路径设置的有问题
拦截的路径一般子在结尾的时候加** 表示所有的。/是匹配子文件夹
拦截所有可以配置为/
*

@Configuration
public class MyWebMvcConfigurer implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {


        //拦截的路径一般子在结尾的时候加** 表示所有的。/*是匹配子文件夹
        //拦截所有可以配置为/**
        registry.addInterceptor(new LoginInterceptor())
                //制定拦截的路径
                .addPathPatterns("/api/*/**")
                //添加不拦截的路径
                .excludePathPatterns("/login", "/css/**", "/images/**", "/js/**", "/login.html");
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

3. controller的测试方法

@RequestMapping("/api/test/testLoginInterceptor")
    public Object testLoginInterceptor() {

        Map map = new HashMap();
        map.put("test","我的拦截器");

        return map;

    }
拦截器的相关问题

1、@Configuration
继承WebMvcConfigurationAdapter(SpringBoot2.X之前旧版本)

SpringBoot2.X 新版本配置拦截器 implements WebMvcConfigurer

2、自定义拦截器 HandlerInterceptor
preHandle:调用Controller某个方法之前
postHandle:Controller之后调用,视图渲染之前,如果控制器Controller出现了异常,则不会执行此方法
afterCompletion:不管有没有异常,这个afterCompletion都会被调用,用于资源清理

3、按照注册顺序进行拦截,先注册,先被拦截

拦截器不生效常见问题:
1)是否有加@Configuration
2)拦截路径是否有问题 ** 和 *
3)拦截器最后路径一定要 “/**”, 如果是目录的话则是 /*/

Filter和拦截器的区别
  • Filter是基于函数回调 doFilter(),而Interceptor则是基于AOP思想
  • Filter在只在Servlet前后起作用,而Interceptor够深入到方法前后、异常抛出前后等
  • Filter依赖于Servlet容器即web应用中,而Interceptor不依赖于Servlet容器所以可以运行在多种环境。
  • 在接口调用的生命周期里,Interceptor可以被多次调用,而Filter只能在容器初始化时调用一次。
Filter和Interceptor的执行顺序

过滤前->拦截前->action执行->拦截后->过滤后