前言
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 开发中,过滤器、拦截器、监听器是三种不同的机制,它们分别用于在应用程序中实现不同的功能。
- 过滤器(Filter):
- 用途:过滤器主要用于在请求到达目标之前或者响应返回客户端之前,对请求和响应进行预处理或者后处理。
- 通俗解释:你可以把过滤器看作是一个位于客户端和服务器之间的"检查站",它可以查看和修改请求的内容,也可以审查和处理服务器返回的响应数据。常见的使用场景是在Web开发中,可以用来实现安全认证、日志记录、数据压缩等功能。
- 拦截器(Interceptor):
- 用途:拦截器也是用于在请求处理过程中进行拦截和处理,不过它更多地用于在业务处理阶段进行干预和控制。
- 通俗解释:拦截器就像一个"旁观者",当你的程序执行到某个特定的点时,拦截器可以插入自己的逻辑。比如在一个Web框架中,拦截器可以在请求进入Controller之前进行权限检查、日志记录等操作,也可以在 Controller 处理完后进行全局异常处理。
- 监听器(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";
}
}
拦截器
- 登录验证,判断用户是否登录
- 权限验证,判断用户是否有权限访问资源,如校验 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 开启过滤器
、拦截器
、监听器
。