在 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()
办法时,运用WebServletHandler
、WebFilterHandler
、WebListenerHandler
一个个对比,契合条件就调用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
的次序往下履行。
总结
虽然两者姓名上、功能上都颇为相似,但他们还是有部分差异的:
- 从履行次序上看:
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 完成原理(源码)
感谢你看到最后,假如你觉得这篇文章对你有帮助,请给我一个免费的大拇哥~