首页 > 编程笔记 > Java笔记 阅读:2

Spring Boot拦截器的用法(非常详细)

Spring MVC 拦截器作为一个附加功能,其重要性远不如 struts 中的拦截器。Spring MVC 拦截器主要是帮我们按照一定规则拦截请求,类似于 Servlet 开发过程中使用的过滤器 Filter,拦截器主要是针对某个业务功能进行预处理和后处理。

拦截器的执行流程如下图所示:


拦截器的本质是 AOP 面向切面编程,也就是说符合横切关注点的功能都可以考虑使用拦截器实现。拦截器有以下几种常见应用场景:

自定义拦截器

在 Spring Boot 项目中自定义拦截器有两个关键步骤:
自定义拦截器,参考代码如下:
/**
* 登录拦截器
* Spring MVC 拦截器作为一个附加功能,重要性远不及 struts 中的拦截器
* 拦截器由 dispatcherServlet 调用,而 JSP 不穿过 dispatcherServlet,所以拦截器不会拦截 JSP
*/
public class LoginInterceptor implements HandlerInterceptor {
    /**
     * 在请求到达具体的 Controller 方法之前执行,且当返回 true 时才执行 Controller 和另外两个方法
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginInterceptor-执行了 preHandle() 方法!");
        // 为测试不拦截请求设置的测试数据
        request.getSession().setAttribute("username", "bobo");
        String username = (String) request.getSession().getAttribute("username");
        if (username == null) {
            response.sendRedirect("/html/login.html");
            return false;
        }
        return true;
    }

    /**
     * 在执行完 Controller 具体方法之后、返回页面之前执行,而且此时还可以修改 ModelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("LoginInterceptor-执行了 postHandle() 方法!");
    }

    /**
     * 在即将到达页面之前执行,此时不可以修改 ModelAndView
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("LoginInterceptor-执行了 afterCompletion() 方法!");
    }
}

HandlerInterceptor 接口中的三个方法说明如下:
1) preHandle()。Controller 接口方法执行前用于拦截请求的方法:
2) postHandle()。在 Controller 中的接口方法执行后、JSP 视图执行前执行:
3) afterCompletion()。在 JSP 执行后执行,不能通过 HttpServletRequest 或 HttpServletResponse 跳转页面。

拦截器配置类,参考代码如下:
/**
* 拦截器配置类
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器 LoginInterceptor
        registry.addInterceptor(new LoginInterceptor())
                // 拦截所有路径
                .addPathPatterns("/**")
                // 添加不被拦截的路径、登录路径和静态资源路径
                .excludePathPatterns("/html/login.html")
                .excludePathPatterns("/css/**");
    }
}

Controller 接口测试类,参考代码如下:
/**
* 拦截器接口测试类
*/
@RestController
@RequestMapping("/interceptor")
public class InterceptorController {
    @GetMapping("/test")
    public String testInterceptor() {
        return "hello interceptor";
    }
}
启动项目,访问路径 http://localhost:8080/interceptor/test,控制台拦截器的执行效果如下图所示:

多拦截器配置

在实际开发过程中,我们可能会需要配置多个拦截器,此时我们首先会考虑到两个问题:
Spring Boot 为我们提供了非常简便的方式,即在配置类中可以通过注册拦截器的顺序来自定义拦截器的执行顺序,详细实现说明如下。

1) 新增拦截器,参考代码如下:
/**
* 拦截器(用于测试拦截器执行顺序)
*/
public class SecondInterceptor implements HandlerInterceptor {

    /**
     * 在请求到达具体的 Controller 方法之前执行,且当返回 true 时才执行 Controller 和另外两个方法
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("SecondInterceptor-执行了 preHandle() 方法!");
        return true;
    }

    /**
     * 在执行完 Controller 具体方法之后、返回页面之前执行,而且还可以修改 ModelAndView
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("SecondInterceptor-执行了 postHandle() 方法!");
    }

    /**
     * 在即将到达页面之前执行,此时不可以修改 ModelAndView
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) throws Exception {
        System.out.println("SecondInterceptor-执行了 afterCompletion() 方法!");
    }
}

2) 修改配置类 InterceptorConfig.java。在配置类中注册拦截器 SecondInterceptor,并且指定先执行拦截器 LoginInterceptor 再执行拦截器 SecondInterceptor。参考代码如下:
/**
* 拦截器配置类
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        /* 可以通过 registry.addInterceptor() 方法控制拦截器注册顺序,
           从而达到自定义拦截器顺序的目的 */

        // 先注册拦截器 LoginInterceptor
        registry.addInterceptor(new LoginInterceptor())
                // 拦截所有路径
                .addPathPatterns("/**")
                // 添加不被拦截的路径、登录路径和静态资源路径
                .excludePathPatterns("/html/login.html")
                .excludePathPatterns("/css/**");

        // 再注册拦截器 SecondInterceptor()
        registry.addInterceptor(new SecondInterceptor())
                // 拦截所有路径
                .addPathPatterns("/**")
                // 添加不被拦截的路径、登录路径和静态资源路径
                .excludePathPatterns("/html/login.html")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/img/**");
    }
}
启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:


对于多个拦截器,正常流程下拦截器方法的执行顺序如下图所示:


非正常情况下,第一个拦截器在 preHandle() 处放行,第二个拦截器在 postHandle() 处被拦截。

修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginInterceptor-执行了 preHandle() 方法!");
        return true;
    }
}

修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
public class SecondInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor-执行了 preHandle() 方法!");
        return false;
    }
}

启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:


非正常情况下的拦截器(第一个拦截器在 preHandle() 处被拦截,在第二个拦截器处放行)。

修改第一个拦截器 LoginInterceptor.java 的代码,参考代码如下:
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("LoginInterceptor-执行了 preHandle() 方法!");
        return false;
    }
}

修改第二个拦截器 SecondInterceptor.java 的代码,参考代码如下:
public class SecondInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SecondInterceptor-执行了 preHandle() 方法!");
        return true;
    }
}
启动项目,在浏览器中访问路径 http://localhost:8080/interceptor/test。两个拦截器的执行结果如下图所示:


对比以上两种非正常情况下执行的拦截器,我们可以得出三点结论:

相关文章