在 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

Spring 中的拦截器与过滤器

这个 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 的次序往下履行。

Spring 中的拦截器与过滤器

总结

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

  • 从履行次序上看:

Spring 中的拦截器与过滤器

Filter 是 Servlet 容器接收到恳求后,可是在调用 Servlet 被调用履行前履行的;而 Interceptor 是 Servlet 被调用后,在恳求抵达 Controller 前履行的

  • 从阻拦粒度来看:Filter 只能对 request、response 进行阻拦;Interceptor 不仅能够对 request、response 进行操作,也能够对 handler、modelAndView 进行操作,具有了对 SpringMVC 组件的操作能力

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

综上所述,在根据 SpringBoot 的项目开发中,假如有需要对恳求阻拦处理的场景,Filter 和 HandlerInterceptor两者之间,优先选择 HandlerInterceptor

参考资料

springboot 中 HandlerInterceptor和Filter差异及运用

关于springboot中增加Filter的办法

【Springboot】阻拦器

Spring MVC HandlerInterceptor 完成原理(源码)

感谢你看到最后,假如你觉得这篇文章对你有帮助,请给我一个免费的大拇哥~