SpringBoot(六):过滤器、拦截器、监听器

郎家岭伯爵 2023年07月17日 371次浏览

前言

SpringBoot 开启过滤器拦截器监听器

实现

理论

过滤器(Filter)

什么是过滤器?

过滤器是在 Servlet 容器中用于处理请求和响应的组件。它可以截获请求和响应,并对它们进行预处理或后处理。过滤器位于请求和目标资源之间,可以修改请求的参数、处理请求头、校验权限等。在 SpringBoot 中,过滤器需要实现 javax.servlet.Filter 接口,并通过配置进行注册。

过滤器的作用:

  • 认证和授权:可以在请求到达目标资源之前验证用户的身份,并进行权限控制。
  • 请求预处理:可以对请求进行预处理,例如请求参数的校验、字符编码的转换等。
  • 响应后处理:可以在响应返回给客户端之前对响应进行处理,例如添加通用的响应头、字符编码的转换等。

拦截器(Interceptor)

什么是拦截器?

拦截器是 Spring 框架提供的一种机制,用于在请求的处理过程中进行拦截和处理。它是基于 Spring 的 AOP(面向切面编程)思想实现的。拦截器位于SpringMVC 的处理器链中,可以对请求进行预处理和后处理,并能够访问处理器方法的参数和返回值。

拦截器的作用:

  • 请求预处理:可以在请求到达处理器方法之前进行预处理,例如参数校验、日志记录等。
  • 后处理:可以在处理器方法执行完毕后进行后续处理,例如记录操作日志、响应结果的封装等。

监听器(Listener)

什么是监听器?

监听器是 Servlet 规范中定义的一种机制,用于监听 Web 应用程序中的事件,并采取相应的动作。在 SpringBoot 中,可以使用 Servlet 规范定义的监听器,也可以使用 Spring 框架提供的 ApplicationListener 接口来实现自定义的监听器。

监听器的作用:

  • 监听应用程序的启动和关闭:可以在应用程序启动和关闭时执行相应的操作,例如初始化数据库连接池、释放资源等。
  • 监听会话的创建和销毁:可以监听会话的创建和销毁事件,进行相应的处理。
  • 监听请求的到达和离开:可以监听请求的到达和离开事件,进行日志记录、性能监控等操作。

过滤器、拦截器和监听器在 SpringBoot 项目中都用于处理请求和响应的生命周期,但它们的实现机制和功能有所不同。过滤器是基于 Servlet 规范的组件,用于对请求和响应进行处理;拦截器是 Spring 框架提供的 AOP 机制,用于拦截和处理请求;监听器是用于监听应用程序中的事件,并采取相应的动作。根据不同的需求,可以选择合适的组件来实现相应的功能。

ChatGPT对三者的解释

在 Java 开发中,过滤器、拦截器、监听器是三种不同的机制,它们分别用于在应用程序中实现不同的功能。

  1. 过滤器(Filter):
  • 用途:过滤器主要用于在请求到达目标之前或者响应返回客户端之前,对请求和响应进行预处理或者后处理。
  • 通俗解释:你可以把过滤器看作是一个位于客户端和服务器之间的"检查站",它可以查看和修改请求的内容,也可以审查和处理服务器返回的响应数据。常见的使用场景是在Web开发中,可以用来实现安全认证、日志记录、数据压缩等功能。
  1. 拦截器(Interceptor):
  • 用途:拦截器也是用于在请求处理过程中进行拦截和处理,不过它更多地用于在业务处理阶段进行干预和控制。
  • 通俗解释:拦截器就像一个"旁观者",当你的程序执行到某个特定的点时,拦截器可以插入自己的逻辑。比如在一个Web框架中,拦截器可以在请求进入Controller之前进行权限检查、日志记录等操作,也可以在 Controller 处理完后进行全局异常处理。
  1. 监听器(Listener):
  • 用途:监听器主要用于监听特定事件的发生,在事件发生时触发自定义的逻辑。
  • 通俗解释:监听器就像一个"耳朵",它专门监听某个对象或者场景,当这个对象或者场景发生了特定的动作或事件,监听器会立即捕捉到并执行相应的动作。在Java中,监听器常用于监听应用程序的生命周期事件、Session 的创建和销毁、属性变化等。

联系:

  • 过滤器、拦截器、监听器都是 JavaWeb 开发中的常见组件,它们都可以用于实现对请求和响应的处理。
  • 它们都可以实现某种程度的拦截和干预,但主要用途和实现方式有所不同。

区别:

  • 过滤器主要用于对请求和响应进行预处理或者后处理,位于客户端和服务器之间。
  • 拦截器主要用于在业务处理阶段进行干预和控制,常用于在请求进入 Controller 前后进行处理。
  • 监听器主要用于监听特定事件的发生,比如应用程序生命周期事件或者 Session 事件。

SpringBoot

过滤器

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

过滤器的创建和销毁都由 Web 服务器负责,Web 应用程序启动的时候,创建过滤器对象,为后续的请求过滤做好准备。

过滤器可以有很多个,一个个过滤器组合起来就成了 FilterChain,也就是过滤器链。

在 Spring 中,过滤器都默认继承了 OncePerRequestFilter,顾名思义,OncePerRequestFilter 的作用就是确保一次请求只通过一次过滤器,而不重复执行。

pom.xml

我们新建一个 SpringBoot 项目,在 pom.xml 文件中引入以下依赖:

<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

MyFilter.java

添加一个过滤器:

package com.langjialing.filterinterceptorlistenerdemo.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;

/**
 * @author 郎家岭伯爵
 * @time 2023/7/17 13:36
 */
@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        long start = System.currentTimeMillis();
        chain.doFilter(request,response);
        System.out.println("Execute cost="+(System.currentTimeMillis()-start));
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

@WebFilter 注解用于将一个类声明为过滤器,urlPatterns 属性用来指定过滤器的 URL 匹配模式,filterName 用来定义过滤器的名字。

MyFilter 过滤器的逻辑非常简单,重写了 Filter 的三个方法,在 doFilter 方法中加入了时间戳的记录。

主启动类

在主启动类上添加注解 @ServletComponentScan,这样过滤器就会自动注册。

package com.langjialing.filterinterceptorlistenerdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;

@SpringBootApplication
// 使 @WebFilter @WebServlet @WebListener 注解生效
@ServletComponentScan
public class FilterInterceptorListenerDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(FilterInterceptorListenerDemoApplication.class, args);
    }

}

controller测试

在 controller 层提供如下接口供测试:

package com.langjialing.filterinterceptorlistenerdemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 郎家岭伯爵
 * @time 2023/7/17 13:38
 */
@RestController
public class TestController {

    @GetMapping("/hello")
    public String hello(){
        return "hello";
    }
}

POSTMAN调用接口

过滤器生效

拦截器

  • 登录验证,判断用户是否登录
  • 权限验证,判断用户是否有权限访问资源,如校验 Token
  • 日志记录,记录请求操作日志(用户 IP,访问时间等),以便统计请求访问量
  • 处理 Cookie、本地化、国际化、主题等
  • 性能监控,监控请求处理时长等

MyInterceptor.java

我们在项目中添加拦截器代码 MyInterceptor

package com.langjialing.filterinterceptorlistenerdemo.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;

/**
 * @author 郎家岭伯爵
 * @time 2023/7/17 13:54
 */
@Slf4j
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle{}...",request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

一个拦截器必须实现 HandlerInterceptor 接口,preHandle 方法是 Controller 方法调用前执行,postHandle 是 Controller 方法正常返回后执行,afterCompletion 方法无论 Controller 方法是否抛异常都会执行。

只有 preHandle 返回 true 的话,其他两个方法才会执行。

如果 preHandle 返回 false 的话,表示不需要调用 Controller 方法继续处理了,通常在认证或者安全检查失败时直接返回错误响应。

InterceptorConfig.java

拦截器配置类:

package com.langjialing.filterinterceptorlistenerdemo.config;

import com.langjialing.filterinterceptorlistenerdemo.interceptor.MyInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * @author 郎家岭伯爵
 * @time 2023/7/17 13:55
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");
    }
}

controller测试

我们依然使用上面的 controller 接口进行测试。在使用 POSTMAN 调用接口后输出如下:

拦截器生效

无论是过滤器还是拦截器,都属于 AOP(面向切面编程) 思想的具体实现。除了这两种实现之外,还有另一种更灵活的 AOP 实现技术,即 Aspect

监听器

根据监听对象可以把监听器分为 3 类:

  • 应用程序监听器(Application Listeners):应用程序监听器是监听整个应用程序的生命周期事件的监听器。它可以监听应用程序的启动、关闭、上下文创建和销毁等事件。在 Java EE 环境中,可以使用 ServletContextListener 接口来实现应用程序监听器。当应用程序启动或关闭时,容器会通知监听器,并调用相应的方法进行处理。

  • 会话监听器(Session Listeners):会话监听器是监听会话对象的创建和销毁事件的监听器。它可以监听用户会话的开始和结束,以及会话属性的变化。在 Java EE 环境中,可以使用 HttpSessionListener 接口来实现会话监听器。当会话创建或销毁时,容器会通知监听器,并调用相应的方法进行处理。

  • 请求监听器(Request Listeners):请求监听器是监听 HTTP 请求和响应的事件的监听器。它可以监听请求的到达和离开,以及请求属性的变化。在 Java EE 环境中,可以使用 ServletRequestListener 接口来实现请求监听器。当请求到达或离开时,容器会通知监听器,并调用相应的方法进行处理。

这些监听器提供了一种机制,可以在应用程序中对特定的事件进行监听和响应。通过实现相应的监听器接口,并在配置文件或代码中进行注册,可以实现对应事件的处理逻辑。监听器在Web开发中具有广泛的应用,用于执行一些初始化操作、记录日志、进行权限控制等任务。

MyListener.java

在项目中添加 MyListener

package com.langjialing.filterinterceptorlistenerdemo.listener;

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.HttpServletRequest;

/**
 * @author 郎家岭伯爵
 * @time 2023/7/17 14:10
 */
@WebListener
public class MyListener implements ServletContextListener, ServletRequestListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("上下文创建");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("上下文销毁");
    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        HttpServletRequest url = (HttpServletRequest) sre.getServletRequest();
        System.out.println("======MyListener Request Initialized========" + url.getRequestURL());

    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        // TODO Auto-generated method stub
        System.out.println("======MyListener Request Destroyed========");

    }
}

controller测试

我们仍然使用刚才创建的 controller 接口进行测试,在使用 POSTMAN 调用接口后:

监听器生效

总结

SpringBoot 开启过滤器拦截器监听器