程序员子龙(Java面试 + Java学习) 程序员子龙(Java面试 + Java学习)
首页
学习指南
工具
开源项目
技术书籍

程序员子龙

Java 开发从业者
首页
学习指南
工具
开源项目
技术书籍
  • 基础

  • JVM

  • Spring

    • 动态代理-CGLIB
    • Hibernate Validator 参数校验优雅实战
    • Jackson序列化json时null转成0或空串
    • 别自己瞎写工具类了!SpringBoot中自带工具类,开发效率增加一倍
    • Spring @Autowired Map
    • SpringBoot 缓存之 @Cacheable 详细介绍与失效时间TTL
    • Spring Security 入门
    • Spring Security原理
    • Spring项目整合MybatisPlus出现org.mybatis.logging.LoggerFactory Not Found 异常
    • Spring在代码中获取bean
    • 别再乱写了,Controller 层代码这样写才足够规范!
    • 非静态变量给静态变量赋值
    • 过滤器与拦截器区别、使用场景
      • 接口重试机制 Spring-Retry
      • 利用cglib动态创建对象或在原对象新增属性
      • 聊聊spring事务失效的场景
      • Spring Event 事件解耦
      • 最全的Spring依赖注入方式
      • Spring初始化之ApplicationRunner、InitializingBean、@PostConstruct 使用详解
      • 为啥不建议用 BeanUtils.copyProperties 拷贝数据
    • 并发编程

    • Mybatis

    • 网络编程

    • 数据库

    • 缓存

    • 设计模式

    • 分布式

    • 高并发

    • SpringBoot

    • SpringCloudAlibaba

    • Nginx

    • 面试

    • 生产问题

    • 系统设计

    • 消息中间件

    • Java
    • Spring
    程序员子龙
    2024-01-29
    目录

    过滤器与拦截器区别、使用场景

    # 过滤器

    过滤器(Filter)是处于客户端与服务器目标资源之间的⼀道过滤技术。

    ● 生活中的过滤器:净⽔器,空气净化器

    ● web中的过滤器:当访问服务器的资源时,过滤器可以将请求拦截下来,完成⼀些特殊的功能。

    # 过滤器作用

    ● 执行是在Servlet之前,客户端发送请求时,会先经过Filter,再到达目标Servlet中;响应时, 会根据执行流程再次反向执行Filter

    ● ⼀般用于完成通用的操作。如:登录验证、统⼀编码处理、敏感字符过滤

    常见的过滤器用途主要包括:对用户请求进行统一认证、对用户的访问请求进行记录和审核、对用户发送的数据进行过滤或替换、转换图象格式、对响应内容进行压缩以减少传输量、对请求或响应进行加解密处理、触发资源访问事件等**。

    # 生命周期

    过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter 接口中定义了三个方法。

    • init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。
    • doFilter() :容器中的每一次请求都会调用该方法, FilterChain 用来调用下一个过滤器 Filter。
    • destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter 的整个生命周期也只会被调用一次。
    @Component
    @WebFilter("/myservlet1")//过滤路径
    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 后置");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    值得注意的是doFilter()方法,**它有三个参数(ServletRequest,ServletResponse,FilterChain),**从前两个参数我们可以发现:过滤器可以完成任何协议的过滤操作!

    在Java中就使用了链式结构。把所有的过滤器都放在FilterChain里边,如果符合条件,就执行下一个过滤器(如果没有过滤器了,就执行目标资源)。

    # 过滤器链和优先级

    过滤器链

    客户端对服务器请求之后,服务器调用Servlet之前会执行⼀组过滤器(多个过滤器),那么这组过滤 器就称为⼀条过滤器链。

    每个过滤器实现某个特定的功能,当第⼀个Filter的doFilter方法被调用时,Web服务器会创建⼀个代 表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发⼈员如果调用了FilterChain对象 的doFilter方法,则Web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter, 如果没有,则调用目标资源。

    优先级:

    ● 如果为注解的话,是按照类全名称的字符串顺序决定作用顺序

    ● 如果web.xml,按照 filter-mapping注册顺序,从上往下

    ● web.xml配置高于注解方式

    # 过滤器路径

    过滤器的过滤路径通常有三种形式:

    • 精确过滤匹配 ,比如/index.jsp /myservlet1

    • 后缀过滤匹配,比如*.jsp、.html、.jpg

    • 通配符过滤匹配/,表示拦截所有。注意过滤器不能使用/匹配。 /aaa/bbb/ 允许

    # 过滤器应用

    @WebFilter(urlPatterns = "/*") 
    @Component
    public class EncodingFilter implements Filter { 
    
            @Override 
            public void init(FilterConfig filterConfig) throws ServletException { 
    
            }
    
            @Override 
            public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, 
                                 FilterChain filterChain) throws IOException, ServletException { 
    
                //统⼀处理请求和响应的乱码 
                servletRequest.setCharacterEncoding("UTF-8"); 
                servletResponse.setContentType("text/html;charset=utf-8"); 
                filterChain.doFilter(servletRequest,servletResponse); 
    
            }
    
            @Override 
            public void destroy() { 
    
            } 
    
    } 
    
    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

    # 拦截器

    # 拦截器作用

    拦截器采用AOP的设计思想, 它跟过滤器类似, 用来拦截处理方法在之前和之后执行一些 跟主业务没有关系的一些公共功能:

    比如:可以实现:权限控制、日志、异常记录、记录方法执行时间.....

    # 自定义拦截器

    SpringMVC提供了拦截器机制,允许运行目标方法之前进行一些拦截工作或者目标方法运行之后进行一下其他相关的处理。自定义的拦截器必须实现 HandlerInterceptor接口。

    HandlerInterceptor 接口中定义了三个方法。

    • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。
    • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler):只有在 preHandle() 方法返回值为true 时才会执行。会在Controller 中的方法调用之后,DispatcherServlet 返回渲染视图之前被调用。 此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
    • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler):只有在 preHandle() 方法返回值为true 时才会执行。在整个请求结束之后, DispatcherServlet 渲染了对应的视图之后执行。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try­catch­finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器才会执行。
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Arrays;
    
    public class MyFirstInterceptor implements HandlerInterceptor {
    
        /**
         * 在处理方法之前执 日志、权限、 记录调用时间
         * @param request 可以在方法请求进来之前更改request中的属性值
         * @param response
         * @param handler 封装了当前处理方法的信息
         * @return true 后续调用链是否执行/ false 则中断后续执行
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            // 在请求映射到对应的处理方法映射,实现类才是HandlerMethod。
            // 如果是视图控制器,实现类ParameterizableViewController
            if(handler instanceof HandlerMethod ) {
                HandlerMethod handMethod = (HandlerMethod) handler;
            }
            /*System.out.println("-------类["+handMethod.getBean().getClass().getName()+"]" +
                    "方法名["+handMethod.getMethod().getName()+"]" +
                    "参数["+ Arrays.toString(handMethod.getMethod().getParameters()) +"]前执行--------preHandle");*/
            System.out.println(this.getClass().getName()+"---------方法后执行,在渲染之前--------------preHandle");
            return true;
        }
    
        /**
         *  如果preHandle返回false则会不会允许该方法
         *  在请求执行后执行, 在视图渲染之前执行
         *  当处理方法出现了异常则不会执行方法
         * @param request
         * @param response 可以在方法执行后去更改response中的信息
         * @param handler  封装了当前处理方法的信息
         * @param modelAndView 封装了model和view.所以当请求结束后可以修改model中的数据或者新增model数据,也可以修改view的跳转
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println(this.getClass().getName()+"---------方法后执行,在渲染之前--------------postHandle");
        }
    
        /**
         * 如果preHandle返回false则会不会允许该方法
         * 在视图渲染之后执行,相当于try catch finally 中finally,出现异常也一定会执行该方法
         * @param request
         * @param response
         * @param handler
         * @param ex  Exception对象,在该方法中去做一些:记录异常日志的功能,或者清除资源
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println(this.getClass().getName()+"---------在视图渲染之后--------------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
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63

    在spring boot 项目中配置

    实现 WebMvcConfigurer 接口 并重写 addInterceptors方法。

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class InterceptorAdapter implements WebMvcConfigurer {
    	
    	@Bean
        public MyFirstInterceptor myInterceptor(){
            return new MyFirstInterceptor();
        }
    	
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(myInterceptor()).addPathPatterns("/**").excludePathPatterns("/*.html");
        }
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     @RequestMapping("/test01")
        public String test01(){
            System.out.println("请求方法执行中...");
            return "admin";
        }
    }
    
    1
    2
    3
    4
    5
    6

    输出结果

    demo.springboot.web.MyFirstInterceptor---------方法后执行,在渲染之前--------------preHandle 请求方法执行中... demo.springboot.web.MyFirstInterceptor---------方法后执行,在渲染之前--------------postHandle demo.springboot.web.MyFirstInterceptor---------在视图渲染之后--------------afterCompletion

    通过运行结果能够发现拦截器的执行顺序如下:

    可以看到先执行拦截器的preHandle方法­­­­》执行目标方法­­­­》执行 拦截器的postHandle方法­­­­》执行页面跳转­­­­》执行拦截器的afterCompletion方法 。

    在配置拦截器的时候有两个需要注意的点:

    1、如果prehandle方法返回值 为false,那么意味着不放行,那么就会造成后续的所有操作都中断

    2、如果执行到方法中出现异常,那么后续流程不会处理但是 afterCompletion方法会执行

    配置多个拦截器

    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    
    public class MySecondInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println(this.getClass().getName()+"‐‐‐‐‐‐‐ >preHandle");
            return true;
        }
    
    
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            System.out.println(this.getClass().getName()+"--------------postHandle");
        }
    
    
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            System.out.println(this.getClass().getName()+"-------------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
    @Configuration
    public class InterceptorAdapter implements WebMvcConfigurer {
    	
    	@Bean
        public MyFirstInterceptor myInterceptor(){
            return new MyFirstInterceptor();
        }
    
        @Bean
        public MySecondInterceptor mySecondInterceptor(){
    	    return new MySecondInterceptor();
        }
    	
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(myInterceptor()).addPathPatterns("/**").excludePathPatterns("/*.html");
            registry.addInterceptor(mySecondInterceptor()).addPathPatterns("/**").excludePathPatterns("/*.html");
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    运行结果:

    demo.springboot.web.MyFirstInterceptor---------方法后执行,在渲染之前--------------preHandle demo.springboot.web.MySecondInterceptor‐‐‐‐‐‐‐ >preHandle 请求方法执行中... demo.springboot.web.MySecondInterceptor--------------postHandle demo.springboot.web.MyFirstInterceptor---------方法后执行,在渲染之前--------------postHandle demo.springboot.web.MySecondInterceptor-------------afterCompletion demo.springboot.web.MyFirstInterceptor---------在视图渲染之后--------------afterCompletion

    大家可以看到对应的效果,谁先执行取决于配置的顺序。

    如果执行的时候核心的业务代码出问题了,那么已经通过的拦截器的 afterCompletion会接着执行。

    # 拦截器跟过滤器的区别

    定义过滤器

    @WebFilter("/*")
    public class MyFilter implements Filter {
        @Override
        public void init(FilterConfig filterConfig) throws ServletException {
    
        }
    
        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            System.out.println("===================过滤器前===================");
            filterChain.doFilter(servletRequest,servletResponse);
            System.out.println("===================过滤器后===================");
        }
    
        @Override
        public void destroy() {
    
        }
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    定义拦截器

    public class MyFirstInterceptor implements HandlerInterceptor {
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    
            System.out.println("---------方法后执行,在渲染之前--------------preHandle");
            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

    运行结果:

    ===================过滤器前=================== ---------方法后执行,在渲染之前--------------preHandle 请求方法执行中... ---------方法后执行,在渲染之前--------------postHandle ---------在视图渲染之后--------------afterCompletion ===================过滤器后===================

    过滤器Filter执行了两次,拦截器Interceptor只执行了一次。

    过滤器和拦截器区别:

    1、过滤器是基于函数回调的,而拦截器是基于java反射的

    在我们自定义的过滤器中都会实现一个 doFilter()方法,这个方法有一个FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。

    public interface FilterChain {
        void doFilter(ServletRequest var1, ServletResponse var2) throws IOException, ServletException;
    }
    
    1
    2
    3

    img

    ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。

    public final class ApplicationFilterChain implements FilterChain {
        @Override
        public void doFilter(ServletRequest request, ServletResponse response) {
                ...//省略
                internalDoFilter(request,response);
        }
     
        private void internalDoFilter(ServletRequest request, ServletResponse response){
        if (pos < n) {
                //获取第pos个filter    
                ApplicationFilterConfig filterConfig = filters[pos++];        
                Filter filter = filterConfig.getFilter();
                ...
                filter.doFilter(request, response, this);
            }
        }
     
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18

    而每个xxxFilter 会先执行自身的 doFilter() 过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChain的doFilter() 方法,以此循环执行实现函数回调。

        @Override
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
    
            filterChain.doFilter(servletRequest, servletResponse);
        }
    
    1
    2
    3
    4
    5

    2、过滤器依赖于servlet容器,而拦截器不依赖与Servlet容器,拦截器依赖SpringMVC

    3、过滤器几乎对所有的请求都可以起作用,而拦截器只能对SpringMVC请求起作用

    4、拦截器可以访问处理方法的上下文,而过滤器不可以

    5、触发时机不同,过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。

    拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

    6、控制执行顺序不同

    实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。

    过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行。

    @Order(Ordered.HIGHEST_PRECEDENCE)
    @Component
    public class MyFilter2 implements Filter {
    
    1
    2
    3

    拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。

     @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**").order(2);
            registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**").order(1);
            registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").order(3);
        }
    
    1
    2
    3
    4
    5
    6

    看到输出结果发现,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

    postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。

    我们要知道controller 中所有的请求都要经过核心组件DispatcherServlet路由,都会执行它的 doDispatch() 方法,而拦截器postHandle()、preHandle()方法便是在其中调用的。

    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
     
            try {
             ...........
                try {
     
                    // 获取可以执行当前Handler的适配器
                    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
                    // Process last-modified header, if supported by the handler.
                    String method = request.getMethod();
                    boolean isGet = "GET".equals(method);
                    if (isGet || "HEAD".equals(method)) {
                        long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
       
                        if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
                            return;
                        }
                    }
                    // 注意: 执行Interceptor中PreHandle()方法
                    if (!mappedHandler.applyPreHandle(processedRequest, response)) {
                        return;
                    }
    
                    // 注意:执行Handle【包括我们的业务逻辑,当抛出异常时会被Try、catch到】
                    mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
    
                    if (asyncManager.isConcurrentHandlingStarted()) {
                        return;
                    }
                    applyDefaultViewName(processedRequest, mv);
    
                    // 注意:执行Interceptor中PostHandle 方法【抛出异常时无法执行】
                    mappedHandler.applyPostHandle(processedRequest, response, mv);
                }
            }
            ...........
        }
    
    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

    看看两个方法applyPreHandle()、applyPostHandle()具体是如何被调用的,就明白为什么postHandle()、preHandle() 执行顺序是相反的了。

    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
            HandlerInterceptor[] interceptors = this.getInterceptors();
            if(!ObjectUtils.isEmpty(interceptors)) {
                for(int i = 0; i < interceptors.length; this.interceptorIndex = i++) {
                    HandlerInterceptor interceptor = interceptors[i];
                    if(!interceptor.preHandle(request, response, this.handler)) {
                        this.triggerAfterCompletion(request, response, (Exception)null);
                        return false;
                    }
                }
            }
    
            return true;
        }
    
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
            HandlerInterceptor[] interceptors = this.getInterceptors();
            if(!ObjectUtils.isEmpty(interceptors)) {
                for(int i = interceptors.length - 1; i >= 0; --i) {
                    HandlerInterceptor interceptor = interceptors[i];
                    interceptor.postHandle(request, response, this.handler, mv);
                }
            }
        }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    发现两个方法中在调用拦截器数组 HandlerInterceptor[] 时,循环的顺序竟然是相反的。。。,导致postHandle()、preHandle() 方法执行的顺序相反。

    总结

    过滤器就是筛选出你要的东西,比如requeset中你要的那部分 拦截器在做安全方面用的比较多,比如终止一些流程

    过滤器(Filter) :可以拿到原始的http请求,但是拿不到你请求的控制器和请求控制器中的方法的信息。

    拦截器(Interceptor):可以拿到你请求的控制器和方法,却拿不到请求方法的参数。

    切片(Aspect): 可以拿到方法的参数,但是却拿不到http请求和响应的对象

    # 监听器

    主要作用是:做一些初始化的内容添加工作、设置一些基本的内容、比如一些参数或者是一些固定的对象等等。

    监听器定义为接口,监听的方法需要事件对象传递进来,从而在监听器上通过事件对象获取得到事件源,对事件源进行修改!

    # 监听器组件

    • 事件源:拥有事件
    • 监听器:监听事件源所拥有的事件(带事件对象参数的)
    • 事件对象:事件对象封装了事件源对象

    当事件源发生某个动作的时候,它会调用事件监听器的方法,并在调用事件监听器方法的时候把事件对象传递进去。

    我们在监听器中就可以通过事件对象获取得到事件源,从而对事件源进行操作!

    # 分类

    listener具体分为八种,能够监听包括request域,session域,application域的产生,销毁和属性的变化;

    ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener分别监听着Context、Session、Request对象属性的变化。

    按监听的对象划分

    可以分为:

    • ServletContext对象的监听器
    • HttpSession对象的监听器
    • ServletRequest对象的监听器

    按监听的事件划分

    可以分为:

    • 对象自身的创建和销毁的监听器
    • 对象中属性的创建和消除的监听器
    • session中的某个对象的状态变化的监听器

    # Servlet监听器

    在Servlet规范中定义了多种类型的监听器,它们用于监听的事件源分别 ServletContext, HttpSession和ServletRequest这三个域对象

    和其它事件监听器略有不同的是,servlet监听器的注册不是直接注册在事件源上,而是由WEB容器负责注册,开发人员只需在web.xml文件中使用<listener>标签配置好监听器。

    ServletContextListener:监听ServletContext对象的创建和销毁

    包含两个方法:

    void contextDestroyed(ServletContextEvent sce) :ServletContext对象被销毁之前会调用

    void contextInitialized(ServletContextEvent sce) :ServletContext对象创建后会调用该方法

    自定义监听器:

    使用注解@WebListener

    import javax.servlet.*;
    import javax.servlet.http.*;
    import avax.servlet.annotation.*;
    
    @WebListener
    @Component
    public class ListenerDemo2 implements ServletContextListener {
    
        public ListenerDemo2() {
        }
    
        @Override
        public void contextInitialized(ServletContextEvent sce) {
            /* This method is called when the servlet context is initialized(when the Web application is deployed). */
            //加载资源 //1,获取ServletContext对象 
            ServletContext servletContext = servletContextEvent.getServletContext(); 
            //2,加载资源⽂件 
            String contextLoadConfig = servletContext.getInitParameter("contextLoadConfig"); 	                         System.out.println("contextLoadConfig:"+contextLoadConfig);
            System.out.println("ListenerDemo2 contextInitialized....");
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
            /* This method is called when the servlet Context is undeployed or Application Server shuts down. */
            System.out.println("ListenerDemo2 contextDestroyed....");
        }
    
    
    }
    
    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

    # 监听对象的创建和销毁

    HttpSessionListener、ServletContextListener、ServletRequestListener分别监控着Session、Context、Request对象的创建和销毁。

    package demo.springboot.web;
    
    import org.springframework.stereotype.Component;
    
    import javax.servlet.ServletContextEvent;
    import javax.servlet.ServletContextListener;
    import javax.servlet.ServletRequestEvent;
    import javax.servlet.ServletRequestListener;
    import javax.servlet.annotation.WebListener;
    import javax.servlet.http.HttpSessionEvent;
    import javax.servlet.http.HttpSessionListener;
    
    @Component
    @WebListener
    public class Listener1 implements ServletContextListener, HttpSessionListener, ServletRequestListener {
    
        public Listener1() {
        }
        
        public void contextInitialized(ServletContextEvent sce) {
            System.out.println("容器创建了");
        }
    
        public void contextDestroyed(ServletContextEvent sce) {
    
            System.out.println("容器销毁了");
        }
    
        @Override
        public void sessionCreated(HttpSessionEvent se) {
    
            System.out.println("Session创建了");
        }
        @Override
        public void sessionDestroyed(HttpSessionEvent se) {
            System.out.println("Session销毁了");
        }
    
    
        @Override
        public void requestDestroyed(ServletRequestEvent servletRequestEvent) {
            
        }
    
        @Override
        public void requestInitialized(ServletRequestEvent servletRequestEvent) {
    
        }
    }
    
    
    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

    监听器监听到ServletContext的初始化了,Session的创建和ServletContext的销毁。(服务器停掉,不代表Session就被销毁了。Session的创建是在内存中的,所以没看到Session被销毁了)

    # 监听对象属性变化

    ServletContextAttributeListener、HttpSessionAttributeListener、ServletRequestAttributeListener分别监听着Context、Session、Request对象属性的变化

    这三个接口中都定义了三个方法来处理被监听对象中的属性的增加,删除和替换的事件,同一个事件在这三个接口中对应的方法名称完全相同,只是接受的参数类型不同。

    • attributeAdded()
    • attributeRemoved()
    • attributeReplaced()
    public class Listener2 implements ServletContextAttributeListener {
    	
    	    @Override
    	    public void attributeAdded(ServletContextAttributeEvent servletContextAttributeEvent) {
    	        System.out.println("Context对象增加了属性");
    	    }
    	
    	    @Override
    	    public void attributeRemoved(ServletContextAttributeEvent servletContextAttributeEvent) {
    	        System.out.println("Context对象删除了属性");
    	
    	    }
    	
    	    @Override
    	    public void attributeReplaced(ServletContextAttributeEvent servletContextAttributeEvent) {
    	        System.out.println("Context对象替换了属性");
    	
    	    }
    	}
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    测试方法

        @GetMapping("/test")
        public String get(@RequestParam("msg") String msg, HttpServletRequest request){
            System.out.println(msg);
            request.setAttribute("aa", "123");
    
            return msg;
        }
    
    1
    2
    3
    4
    5
    6
    7

    Context对象增加了属性

    # 监听Session内的对象

    监听Session内的对象,分别是HttpSessionBindingListener和HttpSessionActivationListener

    # 应用场景

    拦截器是在DispatcherServlet这个servlet中执行的,因此所有的请求最先进入Filter,最后离开Filter。其顺序如下。

    Filter->Interceptor.preHandle->Handler->Interceptor.postHandle->Interceptor.afterCompletion->Filter

    # 拦截器应用场景

    拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:

    • 登录验证,判断用户是否登录。
    • 权限验证,判断用户是否有权限访问资源,如校验token
    • 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量。
    • 处理cookie、本地化、国际化、主题等。
    • 性能监控,监控请求处理时长等。
    • 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现)

    # 过滤器应用场景

    • 过滤敏感词汇(防止sql注入)
    • 设置字符编码
    • URL级别的权限访问控制
    • 压缩响应信息

    # 总结

    过滤器(Filter):当你有一堆东西的时候,你只希望选择符合你要求的某一些东西。定义这些要求的工具,就是过滤器。 拦截器(Interceptor):在一个流程正在进行的时候,你希望干预它的进展,甚至终止它进行,这是拦截器做的事情。 监听器(Listener):当一个事件发生的时候,你希望获得这个事件发生的详细信息,而并不想干预这个事件本身的进程,这就要用到监听器。

    上次更新: 2024/03/11, 15:54:57
    非静态变量给静态变量赋值
    接口重试机制 Spring-Retry

    ← 非静态变量给静态变量赋值 接口重试机制 Spring-Retry→

    最近更新
    01
    一个注解,优雅的实现接口幂等性
    11-17
    02
    MySQL事务(超详细!!!)
    10-14
    03
    阿里二面:Kafka中如何保证消息的顺序性?这周被问到两次了
    10-09
    更多文章>
    Theme by Vdoing | Copyright © 2024-2024

        辽ICP备2023001503号-2

    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式