详解Spring中的拦截器与过滤器

 更新时间:2023年07月05日 14:13:21   作者:gelald  
Filter 过滤器和Interceptor 拦截器是SpringBoot 的 Web 项目开发中长用到的,本文主要来和大家讨论一下 Filter 与 Interceptor 的做法及它们之间的区别,需要的可以参考下

在 SpringBoot 的 Web 项目开发中,如果想实现拦截、过滤的功能,大概会有三种做法:Filter 过滤器、Interceptor 拦截器、AOP 切面编程,而我们今天要讨论的是 Filter 与 Interceptor 的做法及它们之间的区别。

Filter 过滤器

Filter 是 Servlet 中用于拦截请求、过滤请求的一个接口。在以前,我们通常会使用 Filter 来拦截请求设置请求的字符集、判断用户是否登陆、校验权限等等。

其工作原理和核心配置文件 web.xml 息息相关,在配置文件中我们会配置过滤器的名称,以及它过滤的 URL 规则。配置好后,符合过滤规则的请求就会先来到过滤器这里执行 Filter 中的逻辑,以及判断是否能进行下一步的流转。

虽然使用原生的 Servlet 开发的时代大概率已经过去,但是 Servlet 却是 Web 开发基础中的基础,所以 Filter 接口也是能适用于 SpringBoot 项目的。

Filter 方法简单介绍

public interface Filter {  
//Servlet容器(如Tomcat)在初始化这个Filter时调用,一般用于初始化一些资源  
public default void init(FilterConfig filterConfig) throws ServletException {}  
//这个方法是具体执行过滤器逻辑的方法  
//另外chain变量是过滤器链,可以使用这个变量来决定这个请求是否可以向下流转  
public void doFilter(ServletRequest request, ServletResponse response,  
FilterChain chain) throws IOException, ServletException;  
//Servlet容器(如Tomcat)在关闭前会销毁Filter,一般用于资源的释放  
public default void destroy() {}  
}  

SpringBoot 中添加 Filter

SpringBoot 项目中添加 Filter 的步骤主要包括 Filter 定义与注册,添加的方式有 3 种,下面一一做展示

方式一:使用 @WebFilter 注解 + @ServletComponentScan 注解

// 过滤器  
package com.example.demo.filter.one;  
@WebFilter(filterName = "filter-one", urlPatterns = "/bad/*")  
public class FilterOne implements Filter {  
@Override  
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
//执行Filter逻辑  
System.out.println("这是Filter过滤器1号");  
//让请求继续进入Filter链的下一个节点  
chain.doFilter(request, response);  
}  
}  
// 启动类  
@SpringBootApplication  
@ServletComponentScan(basePackages = "com.example.demo.filter.one")  
public class SpringBootDemoApplication {  
public static void main(String[] args) {  
SpringApplication.run(SpringBootDemoApplication.class, args);  
}  
}  

方式二:使用 FilterRegistrationBean 来注册一般过滤器

// 过滤器  
public class FilterTwo implements Filter {  
@Override  
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
//执行Filter逻辑  
System.out.println("这是Filter过滤器2号");  
//让请求继续进入Filter链的下一个节点  
chain.doFilter(request, response);  
}  
}  
// 配置类  
@Configuration  
public class FilterConfiguration {  
@Bean  
public FilterRegistrationBean<FilterTwo> filterRegistrationBean() {  
FilterTwo filterTwo = new FilterTwo();  
FilterRegistrationBean<FilterTwo> filterRegistrationBean = new FilterRegistrationBean<>();  
filterRegistrationBean.setFilter(filterTwo);  
//设置过滤器名、过滤规则  
filterRegistrationBean.setName("filter-two");  
filterRegistrationBean.addUrlPatterns("/bad/*");  
return filterRegistrationBean;  
}  
}  

方式三:使用 DelegatingFilterProxyRegistrationBean 注册已被 Spring 管理的过滤器

// 过滤器  
@Component  
public class FilterThree implements Filter {  
@Override  
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
//执行Filter逻辑  
System.out.println("这是Filter过滤器3号");  
//让请求继续进入Filter链的下一个节点  
chain.doFilter(request, response);  
}  
}  
// 配置类  
@Configuration  
public class FilterConfiguration {  
@Bean  
public DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean() {  
DelegatingFilterProxyRegistrationBean delegatingFilterProxyRegistrationBean = new DelegatingFilterProxyRegistrationBean("filterThree");  
delegatingFilterProxyRegistrationBean.setName("filter-three");  
delegatingFilterProxyRegistrationBean.addUrlPatterns("/bad/*");  
return delegatingFilterProxyRegistrationBean;  
}  
}  

Filter 原理

简单介绍一下三种做法的 Filter 注册原理

方式一,SpringBoot 在启动时,ServletComponentScanRegistrar 类实现了 ImportBeanDefinitionRegistrar 接口,负责把 @ServletComponentScan 中的包路径传递给 ServletComponentRegisteringPostProcessor 类,ServletComponentRegisteringPostProcessor 实现了 BeanFactoryPostProcessor 接口,在调用 postProcessBeanFactory() 方法时,使用 WebServletHandlerWebFilterHandlerWebListenerHandler 一个个对比,符合条件就调用 doHandle() 方法来把 Filter 作为 FilterRegistrationBean 类型的 Bean 注册到 Spring IoC 容器中。

方式二和方式三差异不大

相同点,无论是 FilterRegistrationBean 还是 DelegatingFilterProxyRegistrationBean,他们都是实现了 ServletContextInitializer 接口的,在调用 onStartup() 方法时,抽象基类 AbstractFilterRegistrationBean 会调用 addRegistration() 方法,这个方法就是根据两个子类中返回的 Filter ,添加到 Spring IoC 容器中。

不同点,DelegatingFilterProxyRegistrationBean 通过传入的 targetBeanName 名字,在 Spring IoC 容器中查找该 Fillter 类型的 Bean,并通过 DelegatingFilterProxy 生成基于这个 Bean 的代理 Filter 对象;而 FilterRegistrationBean 则是直接设置一个 Filter ,因此这个 Filter 可以由 Spring IoC 容器管理,也可不用管理。如果一个 Filter 被声明为一个 Bean,而不通过 DelegatingFilterProxyRegistrationBean 添加到 Spring IoC 容器中,那么这个过滤器是无法添加过滤规则的,全局适用。

Filter 在请求中的工作流程

在一次请求里,Filter 不是独立工作,而是以 FilterChain 过滤链的形式来进行过滤,每次请求都根据 URL 的匹配规则来找到符合规则的 Filter ,组装成一条过滤链,请求经过过滤链后才能到达 DispatcherServlet

这个 ApplicationFilterChain 在整个过滤器的工作链路中是一个核心角色,在 createFilterChain() 方法中,会按顺序地添加符合规则的过滤器,组建成一条过滤器链交给 StandardWrapperValve,在调用过滤器逻辑时,直接拿这条过滤器链来做过滤,过滤器中维护了过滤器的顺序,接下来的逻辑就是各个 Filter 的过滤逻辑。执行完各个过滤器后,如果这个请求都通过了过滤,那么最终会来到 DispatcherServlet 中。

HandlerInterceptor 拦截器

拦截器是 Spring 中的内容,它依赖于 Spring 容器,能从 Spring 容器中获取其他 Bean;拦截器提供了更加细颗粒度的拦截功能,更能体现 AOP 思想。

HandlerInterceptor 方法简单介绍

public interface HandlerInterceptor {  
//在请求被处理前(到达Controller前)进行处理,如果返回false,那么请求不往下进行  
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  
throws Exception {  
return true;  
}  
//在请求被处理后(执行完Controller逻辑后)进行处理  
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  
@Nullable ModelAndView modelAndView) throws Exception {  
}  
//在页面渲染结束后执行,一般用于资源释放  
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,  
@Nullable Exception ex) throws Exception {  
}  
}  

SpringBoot 中添加 HandlerInterceptor

与 Filter 类似,添加 HandlerInterceptor 的步骤也分为两步,定义与注册

// 拦截器  
@Component  
public class HandlerInterceptorOne implements HandlerInterceptor {  
@Override  
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  
System.out.println("请求到Controller前-执行 HandlerInterceptor 逻辑");  
return true;  
}  
@Override  
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {  
System.out.println("Controller执行完后-执行 HandlerInterceptor 逻辑");  
}  
@Override  
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  
System.out.println("返回视图前-执行 HandlerInterceptor 逻辑");  
}  
}  
// 配置类  
@Configuration  
public class InterceptorConfiguration implements WebMvcConfigurer {  
@Autowired  
private HandlerInterceptorOne handlerInterceptorOne;  
@Override  
public void addInterceptors(InterceptorRegistry registry) {  
//这册拦截器  
registry.addInterceptor(handlerInterceptorOne)  
//设置拦截的路径  
.addPathPatterns("/bad/*")  
//设置不拦截的路径(排除这些路径)  
.excludePathPatterns("/bad/test");  
}  
}  

HandlerInterceptor 原理

HandlerInterceptor 的工作与 Filter 差别不大,先往容器里注册拦截器,当请求来到 DispatcherServlet 时,调用 getHandler() 方法根据请求的 URL 从容器中取出 URL 符合拦截规则的拦截器,组装成一条拦截器链 HandlerExecutionChain 。然后 DispatcherServlet 按照 preHandle -> handle(Controller) -> postHandle -> afterCompletion 的顺序往下执行。

总结

虽然两者名字上、功能上都颇为相似,但他们还是有部分区别的:

1.从执行顺序上看:

Filter 是 Servlet 容器接收到请求后,但是在调用 Servlet 被调用执行前执行的;而 Interceptor 是 Servlet 被调用后,在请求到达 Controller 前执行的

2.从拦截粒度来看:Filter 只能对 request、response 进行拦截;Interceptor 不仅可以对 request、response 进行操作,也可以对 handler、modelAndView 进行操作,具备了对 SpringMVC 组件的操作能力

3.从依赖从属来看:Filter 依赖于 Servlet 容器;而 Interceptor 不依赖于 Servlet,依赖于 Spring 框架

综上所述,在基于 SpringBoot 的项目开发中,如果有需要对请求拦截处理的场景,Filter 和 HandlerInterceptor两者之间,优先选择 HandlerInterceptor

以上就是详解Spring中的拦截器与过滤器的详细内容,更多关于Spring拦截器过滤器的资料请关注脚本之家其它相关文章!

相关文章

  • Java CountDownLatch与CyclicBarrier及Semaphore使用教程

    Java CountDownLatch与CyclicBarrier及Semaphore使用教程

    对于并发执行,Java中的CountDownLatch是一个重要的类。为了更好的理解CountDownLatch这个类,本文将通过例子和源码带领大家深入解析CountDownLatch与CyclicBarrier及Semaphore的原理,感兴趣的可以学习一下
    2023-01-01
  • Java实现图形化界面的日历

    Java实现图形化界面的日历

    这篇文章主要介绍了Java实现图形化界面的日历,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • java 通过聚合查询实现elasticsearch的group by后的数量

    java 通过聚合查询实现elasticsearch的group by后的数量

    这篇文章主要介绍了java 通过聚合查询实现elasticsearch的group by后的数量,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • java单例五种实现模式解析

    java单例五种实现模式解析

    这篇文章主要介绍了java单例五种实现模式解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • 利用栈使用简易计算器(Java实现)

    利用栈使用简易计算器(Java实现)

    这篇文章主要为大家详细介绍了Java利用栈实现简易计算器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-09-09
  • SpringBoot对接小程序微信支付的实现

    SpringBoot对接小程序微信支付的实现

    本文主要介绍了SpringBoot对接小程序微信支付的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧<BR>
    2023-09-09
  • java实现在普通类中注入service或mapper

    java实现在普通类中注入service或mapper

    这篇文章主要介绍了java实现在普通类中注入service或mapper的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • Java中的数组使用详解及练习

    Java中的数组使用详解及练习

    数组是Java程序中最常见的一种数据结构,它能够将相同类型的数据用一个标识符封装到一起,构成一个对象序列或基本数据类型,这篇文章主要给大家介绍了关于Java中数组使用详解及练习的相关资料,需要的朋友可以参考下
    2024-03-03
  • Spring + Mybatis 项目实现动态切换数据源实例详解

    Spring + Mybatis 项目实现动态切换数据源实例详解

    这篇文章主要介绍了Spring + Mybatis 项目实现动态切换数据源的相关资料,需要的朋友参考下吧
    2017-04-04
  • Intellij IDEA创建spring-boot项目的图文教程

    Intellij IDEA创建spring-boot项目的图文教程

    本文通过图文并茂的形式给大家介绍了Intellij IDEA创建spring-boot项目的教程,本文给大家介绍的非常详细,具有参考借鉴价值,需要的朋友参考下吧
    2018-01-01

最新评论