• 拦截器与过滤器的区别


    优质博文:IT-BLOG-CN

    拦截器Interceptor和过滤器Filter都是基于AOP(Aspect Oriented Programming,面向切面编程)思想实现的,用来解决项目中某一类问题的两种“工具”,两者在使用上有时候可能会分不清,希望能够通过本章解决这个痛点。

    一、拦截器

    拦截器InterceptorSpringMVC中实现的一种基于Java反射(动态代理)机制的方法增强工具,是面向切面AOP编程中应用的一种统一处理方案。拦截器的实现是继承HandlerInterceptor接口,并实现接口的preHandlepostHandleafterCompletion方法,对目标方法进行增强。可以访问action上下文、堆栈里的对象,可以多次被调用。
    【1】preHandle:请求方法前置拦截,该方法会在Controller处理之前进行调用,Spring中可以有多个Interceptor,这些拦截器会按照设定的Order顺序调用,当有一个拦截器在preHandle中返回false的时候,请求就会终止。
    【2】postHandlepreHandle返回结果为true时,在Controller方法执行之后,视图渲染之前被调用。
    【3】afterCompletion:在preHandle返回ture,并且整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。此方法主要用来进行资源清理。

    @Component
    public class UserInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 获取用户信息
            EmpsInformationEntity ee = CommonTool.getEmpsInfo();
    
            if (ee == null || StringUtils.isEmpty(ee.getEid())) {
                // 跳转登录
                redirectToLoginPage(request, response);
                return false;
            }
    
            // 模块权限
            PermissionConfig permission = getPermissionConfig(handler);
            boolean hasPermission = checkPermission(request, response, permission);
            if (!hasPermission) {
                if (isAjax(request, permission)) {
                    response.getWriter().write("permission error");
                } else {
                    List<String> codes = getModuleCode(permission);
                    if (CollectionUtils.isNotEmpty(codes)) {
                        response.sendRedirect(String.format(screenPopupConfig.getIamErrorPage(), codes.get(0), ee.getEid()));
                        return false;
                    }
                    response.sendRedirect(request.getContextPath() + "/noPermisson");
                }
                return false;
            }
    
            request.setAttribute("Eid", ee.getEid());
            request.setAttribute("Ename", ee.getEmpName());
            /**
             * 多语言环境
             */
            if (request.getRequestURI().indexOf("/lastOrderId") == -1) {
                // 取值
                CultureHelper.initPreRequest(request, response, ee.getDept());
            }
            accessMetric(ee, request);
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println("postHandle");
        }
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println("afterCompletion");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    编写完拦截器之后,通过一个配置类设置拦截器,并且可以通过addPathPatternsexcludePathPatterns执行哪些请求需要被拦截,哪些不需要被拦截。

    @Configuration
    public class MvcConfig implements WebMvcConfigurer {
        @Autowired
        private UserInterceptor userInterceptor;
    
        // 或者通过@Bean注入
        /** @Bean
         * public UserInterceptor getUserInterceptor() {
         *     return new UserInterceptor();
         **/ }
    
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(userInterceptor)
                    .addPathPatterns("/**")
                    .excludePathPatterns("/error");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    应用场景: 主要是用在插件上,扩展件上比如hibernate spring struts2等有点类似面向切片的技术,在用之前先要在配置文件即xml文件里声明一段的那个东西。
    【1】日志记录:记录请求操作日志(用户ip,访问时间等)
    【2】权限检查:判断用户是否有权限访问资源,如校验token日志记录
    【3】性能监控:记录请求->响应时间,preHandle:记录开始时间,afterCompletion:记录结束时间,开始时间相减去结束时间
    【4】登录验证:判断用户是否登录
    【5】sign校验,封禁校验等
    【6】处理cookie,主题,国际化,本地化等
    【7】filter可以实现的功能intercepter基本上都能实现

    二、过滤器

    过滤器Filter基于Servlet实现,过滤器的主要应用场景是对字符编码、跨域等问题进行过滤。Servlet的工作原理是拦截配置好的客户端请求,然后对RequestResponse进行处理。Filter过滤器随着web应用的启动而启动,只初始化一次。Filter的使用比较简单,继承Filter接口,实现对应的initdoFilter以及destroy方法即可。
    【1】init:在容器启动时调用初始化方法,只会初始化一次。
    【2】doFilter:每次请求都会调用doFilter方法,通过FilterChain调用后续的方法。
    【3】destroy:当容器销毁时,执行destory方法,只会被调用一次。

    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
            System.out.println("初始化拦截器");
        }
    
        @Override
        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
            //做一些处理
            chain.doFilter(request,response);
        }
    
        @Override
        public void destroy() {
            System.out.println("销毁拦截器");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    Filter配置类

    @Slf4j
    @Configuration
    public class FilterConfiguration {//不拦截路径
        private static List<String> exclusionUrlList=new ArrayList<>();
        //拦截路径
        private static List<String> inclusionUrlList=new ArrayList<>();
    
        static {
            exclusionUrlList.add("/favicon.ico");
            exclusionUrlList.add("/**/*.css");
            exclusionUrlList.add("/**/*.js");
            exclusionUrlList.add("/ok");
            inclusionUrlList.add("/api/**");
        }
    
        @Bean
        public FilterRegistrationBean filterRegistration() {
            FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
            registration.setFilter(new MyFilter());
            registration.addInitParameter(FILTER_INIT_PARAM_EXCLUSION_URLS,String.join(",", exclusionUrlList));
            registration.addInitParameter(AJAX_URL_PATTERNS,String.join(",", inclusionUrlList));
            registration.addUrlPatterns("/*");
            registration.setName("testFilter");
         return registration;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    过滤器Filter的三种配置方式:
    【1】通过@WebFilter注解配置

    1.初始化Filter
    
    @WebFilter(urlPatterns = "/test001")
    @Order(1) //order值越小,过滤器越靠前,此处配置无效
    public class MyFilter implements Filter {
        @Override
        public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            System.out.println("##############TestFilter init##############");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            //在DispatcherServlet之前执行
            System.out.println("##############doFilter before##############");
            filterChain.doFilter(servletRequest, servletResponse);
            // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
            System.out.println("##############doFilter after##############");
        }
    
        @Override
        public void destroy() {
            System.out.println("##############TestFilter destroy##############");
        }
    }
    
    //2.在启动类添加 @ServletComponentScan
    
    @SpringBootApplication 
    @ServletComponentScan 
    public class TestbootApplication { 
      public static void main(String[] args) { 
        SpringApplication.run(TestbootApplication.class, args); 
      } 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    【2】通过@Bean来配置:初始化Filter

    @Component
    public class MyFilter implements Filter {
        @Override
        public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
            System.out.println("##############Filter3 init##############");
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            //在DispatcherServlet之前执行
            System.out.println("##############doFilter3 before##############");
            filterChain.doFilter(servletRequest, servletResponse);
            // 在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后
            System.out.println("##############doFilter3 after##############");
        }
    
        @Override
        public void destroy() {
            System.out.println("##############Filter3 destroy##############");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    注册到config

    @Configuration
    public class FilterConfig {
    
        @Bean
        public FilterRegistrationBean testFilter3RegistrationBean() {
            FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter());
            registration.addUrlPatterns("/hello");
            registration.setOrder(1); // 值越小越靠前,此处配置有效
            return registration;
        }
    
        @Bean
        public FilterRegistrationBean testFilter4RegistrationBean() {
            FilterRegistrationBean registration = new FilterRegistrationBean(new MyFilter1());
            registration.addUrlPatterns("/hello");
            registration.setOrder(2);
            return registration;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    【3】Spring MVCweb.xml进行配置:1、初始化Filter;2、web.xml文件中配置Filter

    <filter>
        <filter-name>CharacterEncodingFilterfilter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
        <init-param>
            <param-name>encodingparam-name>
            <param-value>UTF-8param-value>
        init-param>
        <init-param>
            <param-name>forceEncodingparam-name>
            <param-value>trueparam-value>
        init-param>
    filter>
    
    <filter-mapping>
        <filter-name>CharacterEncodingFilterfilter-name>
        <url-pattern>/*url-pattern>
    filter-mapping>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    应用场景: 传入的request,response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者strutsaction进行业务逻辑处理。例如在过滤器中修改字符编码CharacterEncodingFilter、在过滤器中修改HttpServletRequest的一些参数XSSFilter(自定义过滤器),如:过滤低俗文字、危险字符、敏感词过滤、响应信息压缩、控制权限、控制转向、做一些业务逻辑判断等

    三、拦截器与过滤器的区别

    拦截器与过滤器执行时机不同: 请求进入Servlet之前,过滤器的doFilter方法进行过滤,进入Servlet容器之后,执行Controller方法之前,拦截器的preHandle方法进行拦截,执行Controller之后,视图渲染之前postHandle方法进行拦截,请求结束之后,拦截器的postHandle方法执行。

    拦截器基于Java发射机制实现,过滤器基于函数回调方式实现。在实际应用中拦截器主要用于权限控制、日志打印、参数校验,过滤器主要用于跨域问题解决、编码转换

    在这里插入图片描述

    区别过滤器拦截器备注
    配置位置配置在web.xml配置在springmvc.xml
    定义位置Filter定义在java.servlet包下接口HandlerInterceptor定义在org.springframework.web.servlet包下
    使用范围FilterServlet规范规定的而拦截器既可以用于Web程序,也可以用于ApplicationSwing程序中。
    遵循规范Filter是遵循Servlet规范而拦截器是在Spring容器内的,是Spring框架支持的。
    实现方式Filter基于函数回掉Interceptor基于java反射
    作用Filter在只在Servlet前后起作用,Filter通常不考虑servlet的实现拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。允许用户介入hook into请求的生命周期Spring构架的程序中,要优先使用拦截器。几乎所有Filter能够做的事情,interceptor都能够轻松的实现
    调用方Filter是被Server(like Tomcat)调用Interceptor是被Spring调用因此Filter总是优先于Interceptor执行
    Spring关系Filter不能够使用Spring容器资源Interceptor是被Spring调用Spring中使用Interceptor更容易
  • 相关阅读:
    Mysql开启binlog 和 打开gtid_mode
    踩坑笔记 NFS坑-2个pod读写文件延迟问题
    Spring源码解析—— IOC默认标签解析(下)
    2022暑假牛客多校2(G/K/D)
    跨平台编译工具--CMake上手教程
    【复】一次流量分析经历
    Linux中系统时间同步
    Freemarker
    【QSPI】 什么是QSPI SPI和QSPI异同
    PCL点云自定义裁剪的两种思路
  • 原文地址:https://blog.csdn.net/zhengzhaoyang122/article/details/134431347