zoukankan      html  css  js  c++  java
  • SpringBoot 过滤器,拦截器初步学习整理(有示例代码)

    引言

    关于两者的理论知识,网上有太多就补贴在本章了。该文章主要以代码的形式说明,方便新手理解。
    这里也是新手学习时整理的文档,主要针对于新手的,如果有不正确的地方希望加一指正。

    两者的区别

    1. Filter 是基于 函数回调的,而 Interceptor 则是基于 Java反射 和 动态代理。
    2. Filter 依赖于 Servlet 容器,而 Interceptor 它依赖于web框架。
    3. Filter 对几乎 所有的请求 起作用,而 Interceptor 只对 Controller 对请求起作用。

    执行顺序

    对于自定义 Servlet 对请求分发流程:

    1. Filter 过滤请求处理;
    2. Servlet 处理请求;
    3. Filter 过滤响应处理。

    对于自定义 Controller 的请求分发流程:

    1. Filter 过滤请求处理:
    2. Interceptor 拦截请求处理;
    3. 对应的 HandlerAdapter 处理请求;
    4. Interceptor 拦截响应处理;
    5. Interceptor 的最终处理;
    6. Filter 过滤响应处理。

    代码示例

    过滤器

    一个 Servlet 请求可以经由多个 Filter 进行过滤,最终由 Servlet 处理并响应客户端。这里就建两个过滤器测试。

    package com.blackcat.demo.filter;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    import java.io.IOException;
    
    import static org.springframework.util.ObjectUtils.isEmpty;
    
    /**
     * <p> 描述 :过滤器
     * @author : blackcat
     * @date : 2020/5/20 14:04
     *
     * Filter 对 用户请求 进行 预处理,接着将请求交给 Servlet 进行 处理 并 生成响应,
     *  最后 Filter 再对 服务器响应 进行 后处理。
     * Filter 是可以复用的代码片段,常用来转换 HTTP 请求、响应 和 头信息。
     * Filter 不像 Servlet,它不能产生 响应,而是只 修改 对某一资源的 请求 或者 响应。
     *
     * 过滤器就是筛选出你要的东西,比如requeset中你要的那部分
     * 一个 Servlet 请求可以经由多个 Filter 进行过滤,最终由 Servlet 处理并响应客户端。
     */
    @Slf4j
    @WebFilter(filterName = "firstIndexFilter",// filter名称
        displayName = "firstIndexFilter",
        urlPatterns = {"/index/*"},// 路径匹配
        initParams = @WebInitParam(
            name = "firstIndexFilterInitParam",
            value = "io.ostenant.springboot.sample.filter.FirstIndexFilter")
    )
    public class FirstIndexFilter implements Filter {
    
        /**
         * <p> 描述 : 初始化时,会执行 init() 方法
         * @author : blackcat
         * @date  : 2020/5/20 14:16
        */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("注册新筛选器 {}", filterConfig.getFilterName());
        }
    
        /**
         * <p> 描述 : 过滤请求
         * @author : blackcat
         * @date  : 2020/5/20 14:24
         * @param request 未到达 Servlet 的 HTTP 请求;
         * @param response 由 Servlet 处理并生成的 HTTP 响应;
         * @param chain 过滤器链 对象,可以按顺序注册多个 过滤器。
         * @return void
         * 每次请求路径匹配 urlPatterns 配置的路径时,就会进入 doFilter() 方法进行具体的 请求 和 响应过滤。
         * 一个 过滤器链 对象可以按顺序注册多个 过滤器。符合当前过滤器过滤条件,即请求 过滤成功 直接放行,则交由下一个 过滤器 进行处理。
         * 所有请求过滤完成以后,由 IndexHttpServlet 处理并生成 响应,然后在 过滤器链 以相反的方向对 响应 进行后置过滤处理。
         */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            log.info("FirstIndexFilter预过滤请求");
            // 当 HTTP 请求携带 filter1 参数时,请求会被放行;否则,直接 过滤中断,结束请求处理。
            String filter = request.getParameter("filter1");
            if (isEmpty(filter)) {
                response.getWriter().println("请设置请求参数 "filter1"");
                log.info("请设置请求参数 filter1"+filter);
                return;
            }
            chain.doFilter(request, response);
            log.info("FirstIndexFilter对响应进行后筛选");
        }
    
        @Override
        public void destroy() {
            log.info("注销过滤器 {}", getClass().getName());
        }
    }
    package com.blackcat.demo.filter;
    
    import lombok.extern.slf4j.Slf4j;
    
    import javax.servlet.*;
    import javax.servlet.annotation.WebFilter;
    import javax.servlet.annotation.WebInitParam;
    import java.io.IOException;
    
    import static org.springframework.util.ObjectUtils.isEmpty;
    
    /**
     * <p> 描述 :过滤器
     * @author : blackcat
     * @date : 2020/5/20 14:20
     *
     * 一个 Servlet 请求可以经由多个 Filter 进行过滤,最终由 Servlet 处理并响应客户端。
     */
    @Slf4j
    @WebFilter(filterName = "secondIndexFilter",// filter名称
            displayName = "secondIndexFilter",
            urlPatterns = {"/index/*"},// 路径匹配
            initParams = @WebInitParam(
                    name = "secondIndexFilterInitParam",
                    value = "io.ostenant.springboot.sample.filter.SecondIndexFilter")
    )
    public class SecondIndexFilter implements Filter {
    
        /**
         * <p> 描述 : 初始化时,会执行 init() 方法
         * @author : blackcat
         * @date  : 2020/5/20 14:16
         */
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("注册新筛选器 {}", filterConfig.getFilterName());
        }
    
        /**
         * <p> 描述 : 过滤请求
         * @author : blackcat
         * @date  : 2020/5/20 14:24
         * @param request 未到达 Servlet 的 HTTP 请求;
         * @param response 由 Servlet 处理并生成的 HTTP 响应;
         * @param chain 过滤器链 对象,可以按顺序注册多个 过滤器。
         * @return void
         * 每次请求路径匹配 urlPatterns 配置的路径时,就会进入 doFilter() 方法进行具体的 请求 和 响应过滤。
         * 一个 过滤器链 对象可以按顺序注册多个 过滤器。符合当前过滤器过滤条件,即请求 过滤成功 直接放行,则交由下一个 过滤器 进行处理。
         * 所有请求过滤完成以后,由 IndexHttpServlet 处理并生成 响应,然后在 过滤器链 以相反的方向对 响应 进行后置过滤处理。
        */
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
                throws IOException, ServletException {
            log.info("SecondIndexFilter预过滤请求");
            // 当 HTTP 请求携带 filter1 参数时,请求会被放行;否则,直接 过滤中断,结束请求处理。
            String filter = request.getParameter("filter2");
            if (isEmpty(filter)) {
                response.getWriter().println("请设置请求参数 "filter2"");
                return;
            }
            chain.doFilter(request, response);
            log.info("SecondIndexFilter对响应进行后筛选");
        }
    
        @Override
        public void destroy() {
            log.info("注销过滤器 {}", getClass().getName());
        }
    }

    拦截器

    类似面向切面编程中的切面通知,我们通过动态代理对一个 service() 方法添加通知进行功能增强。

    比如说在方法执行前进行初始化处理,在方法执行后进行 后置处理。

    拦截器 的思想和 AOP 类似,区别就是拦截器只能对 Controller 的 HTTP 请求进行拦截。

    package com.blackcat.demo.interceptor;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import static org.springframework.util.ObjectUtils.isEmpty;
    
    /**
     * <p> 描述 :拦截器
     * @author : blackcat
     * @date : 2020/5/20 14:28
     *
     * 类似 面向切面编程 中的 切面 和 通知,我们通过 动态代理 对一个 service() 方法添加 通知 进行功能增强。比如说在方法执行前进行 初始化处理,在方法执行后进行 后置处理。
     * 拦截器 的思想和 AOP 类似,区别就是 拦截器 只能对 Controller 的 HTTP 请求进行拦截。
     *
     * 拦截器在做安全方面用的比较多,比如终止一些流程
     * 拦截器 Interceptor 只对 Handler 生效。Spring MVC 会为 Controller 中的每个 请求方法 实例化为一个 Handler对象,
     * 由 HandlerMapping 对象路由请求到具体的 Handler,然后由 HandlerAdapter 通过反射进行请求 处理 和 响应,这中间就穿插着 拦截处理。
     */
    @Slf4j
    public class FirstIndexInterceptor implements HandlerInterceptor {
    
        /**
         * <p> 描述 : 在请求处理之前进行调用(Controller方法调用之前
         * @author : blackcat
         * @date  : 2020/5/20 14:32
         *
         * controller 接收请求、处理 request 之前执行,返回值为 boolean,
         * 返回值为 true 时接着执行 postHandle() 和 afterCompletion() 方法;
         * 如果返回 false 则 中断 执行。
        */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            log.info("进入preHandle");
            String interceptor = request.getParameter("interceptor1");
            if (isEmpty(interceptor)) {
                response.getWriter().println("请设置请求参数 "interceptor1"");
                return false;
            }
            return true;
        }
    
        /**
         * <p> 描述 : 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)
         * @author : blackcat
         * @date  : 2020/5/20 14:33
        */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            log.info("进入postHandle");
        }
    
        /**
         * <p> 描述 : 在整个请求结束之后被调用,也就是在DispatcherServlet 渲染了对应的视图之后执行(主要是用于进行资源清理工作)
         * @author : blackcat
         * @date  : 2020/5/20 14:33
        */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            log.info("进入afterCompletion");
        }
    }

    注册拦截器

    前提:springboot启动类中@ServletComponentScan开启扫描

    package com.blackcat.demo.config;
    
    import com.blackcat.demo.interceptor.FirstIndexInterceptor;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * <p> 描述 :Web配置
     * @author : blackcat
     * @date : 2020/5/20 14:37
     */
    @Slf4j
    @Configuration
    public class WebConfiguration implements WebMvcConfigurer {
    
        /**
         * <p> 描述 : 注册拦截器
         * @author : blackcat
         * @date  : 2020/5/20 14:39
         * 在 Spring Boot 中 配置拦截器,只需要实现 WebMvcConfigurer 接口,
         * 在 addInterceptors() 方法中通过 InterceptorRegistry 添加 拦截器 和 匹配路径 即可。
        */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            // 注册拦截器
            InterceptorRegistration first = registry.addInterceptor(new FirstIndexInterceptor());
            // 添加拦截请求
            first.addPathPatterns("/index/**");
            // 添加不拦截的请求
            first.excludePathPatterns("/login");
    
            // 简写格式
    //        registry.addInterceptor(new FirstIndexInterceptor()).addPathPatterns("/index/**");
            log.info("注册拦截器");
        }
    }

     IndexController

    package com.blackcat.demo.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    /**
     * <p> 描述 :控制器
     * @author : blackcat
     * @date : 2020/5/20 14:27
     */
    @Slf4j
    @Controller
    @RequestMapping("/index")
    public class IndexController {
    
        /**
         * <p> 描述 :
         * @author : blackcat
         * @date  : 2020/5/22 12:57
         *
         * 测试连接:
         * http://localhost:8003/index/get?filter1=123&filter2=456&interceptor1=789
         * 少一个参数就无法返回 ‘ok’
        */
        @RequestMapping(value="/get")
        @ResponseBody
        public String get() {
            return "ok";
        }
    }

    代码结构

    因为本文主要说明过滤器拦截器,Servlet跟Listener不多说,可以看注释。

    测试

    路径的拦截是根据 @WebFilter 注解 urlPatterns 进行匹配
    例如:/index/* 就会拦截 index 下所有方法

    启动项目

    项目运行会根据WebConfiguration 注册拦截器。测试工具postman。

     过滤器测试

    如果有多个过滤器,一个拦截器通过后,自动进入下一拦截器。必须所有过滤器全通过之后,才会执行拦截器代码。

    FirstIndexFilter

    测试链接:http://localhost:8003/index/get

    测试结果:过滤器filter1没有通过。后续代码不执行。

     

    SecondIndexFilter

    测试链接:http://localhost:8003/index/get?filter1=123

    测试结果:过滤器filter2没有通过。后续代码不执行。(原理同上)

     

     拦截器测试

    拦截原理同过滤器相同。

    测试链接:http://localhost:8003/index/get?filter1=123&filter2=456

    测试结果:拦截器interceptor1未通过。后续代码不执行。

     测试链接:http://localhost:8003/index/get?filter1=123&filter2=456&interceptor1=789

     测试结果:拦截器interceptor1通过。执行访问方法返回结果。

    示例源码

    https://gitee.com/kylin_lawliet/learn-springboot/tree/master/filter-interceptor

  • 相关阅读:
    修改大表结构注意事项
    短信猫 Mysql ODBC问题
    各互联网公司UDE分享
    MySQL 取分类后的前n条
    批处理当前日期
    Fixed Event Scheduler No data zero rows fetched, selected, or processed
    数据库设计注意事项
    本月,下一月, 上一月 的 1号, 最后一号
    自动化测试编程语言的选择
    QTP描述编程中使用正则表达式
  • 原文地址:https://www.cnblogs.com/Kylin-lawliet/p/13373399.html
Copyright © 2011-2022 走看看