准备环境


我们在项目中同时配置 拦截器 和 过滤器。

1、过滤器 (Filter)过滤器的配置比较简单,直接实现Filter 接口即可,也可以通过@WebFilter注解实现对特定URL拦截,看到Filter接口中定义了三个方法。

  • init() :该方法在容器启动初始化过滤器时被调用,它在 Filter 的整个生命周期只会被调用一次。注意:这个方法必须执行成功,否则过滤器会不起作用。

  • doFilter() :容器中的每一次请求都会调用该方法, FilterChain用来调用下一个过滤器 Filter。

  • destroy(): 当容器销毁 过滤器实例时调用该方法,一般在方法中销毁或关闭资源,在过滤器 Filter的整个生命周期也只会被调用一次

 @Componentpublic class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {​System.out.println("Filter 前置");}​@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {​System.out.println("Filter 处理中");filterChain.doFilter(servletRequest, servletResponse);}​@Overridepublic void destroy() {​System.out.println("Filter 后置");}}

2、拦截器 (Interceptor)拦截器它是链式调用,一个应用中可以同时存在多个拦截器Interceptor, 一个请求也可以触发多个拦截器 ,而每个拦截器的调用会依据它的声明顺序依次执行。

首先编写一个简单的拦截器处理类,请求的拦截是通过HandlerInterceptor 来实现,看到HandlerInterceptor接口中也定义了三个方法。

preHandle() :这个方法将在请求处理之前进行调用。注意:如果该方法的返回值为false ,将视为当前请求结束,不仅自身的拦截器会失效,还会导致其他的拦截器也不再执行。

postHandle():只有在preHandle() 方法返回值为true 时才会执行。会在Controller中的方法调用之后,DispatcherServlet返回渲染视图之前被调用。 有意思的是:postHandle()方法被调用的顺序跟 preHandle()是相反的,先声明的拦截器 preHandle()方法先执行,而postHandle()方法反而会后执行。

afterCompletion():只有在 preHandle()方法返回值为true时才会执行。在整个请求结束之后,DispatcherServlet渲染了对应的视图之后执行。

 @Componentpublic class MyInterceptor implements HandlerInterceptor {   /**** 在请求处理之前进行调用(Controller方法调用之前)*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return HandlerInterceptor.super.preHandle(request, response, handler);//如果设置为false时,被请求时,拦截器执行到此处将不会继续操作//如果设置为true时,请求将会继续执行后面的操作}​/**** 请求处理之后进行调用,但是在视图被渲染之前(Controller方法调用之后)*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}​/**** 整个请求结束之后被调用,也就是在DispatchServlet渲染了对应的视图之后执行(主要用于进行资源清理工作)*/@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}

将自定义好的拦截器处理类进行注册,并通过addPathPatterns、excludePathPatterns等属性设置需要拦截或需要排除的 URL。

 @Configurationpublic class MyMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**");registry.addInterceptor(new MyInterceptor1()).addPathPatterns("/**");}}

区别


过滤器 和 拦截器 均体现了`AOP`的编程思想,都可以实现诸如日志记录、登录鉴权等功能,但二者的不同点也是比较多的,接下来一一说明。

1、实现原理不同

过滤器和拦截器 底层实现方式大不相同,过滤器 是基于函数回调的,拦截器 则是基于Java的反射机制(动态代理)实现的。

这里重点说下过滤器!

在我们自定义的过滤器中都会实现一个 doFilter()方法,这个方法有一个FilterChain 参数,而实际上它是一个回调接口。ApplicationFilterChain是它的实现类, 这个实现类内部也有一个 doFilter() 方法就是回调方法。

 public interface FilterChain {void doFilter(ServletRequestvar1, ServletResponsevar2) throwsIOException, ServletException;}

ApplicationFilterChain里面能拿到我们自定义的xxxFilter类,在其内部回调方法doFilter()里调用各个自定义xxxFilter过滤器,并执行 doFilter() 方法。

 public final class ApplicationFilterChain implements FilterChain {@Overridepublic void doFilter(ServletRequest request, ServletResponse response) {...//省略internalDoFilter(request,response);}​private void internalDoFilter(ServletRequest request, ServletResponse response){if (pos < n) {//获取第pos个filter    ApplicationFilterConfig filterConfig = filters[pos++];        Filter filter = filterConfig.getFilter();...filter.doFilter(request, response, this);}}}

而每个xxxFilter会先执行自身的 doFilter()过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse),也就是回调ApplicationFilterChain的
doFilter()方法,以此循环执行实现函数回调。

 @Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {​filterChain.doFilter(servletRequest, servletResponse);}

2、使用范围不同

我们看到过滤器 实现的是 javax.servlet.Filter 接口,而这个接口是在Servlet规范中定义的,也就是说过滤器Filter 的使用要依赖于Tomcat等容器,导致它只能在web程序中使用。

而拦截器(Interceptor) 它是一个Spring组件,并由Spring容器管理,并不依赖Tomcat等容器,是可以单独使用的。不仅能应用在web程序中,也可以用于Application、Swing等程序中。

3、触发时机不同

过滤器 和 拦截器的触发时机也不同,我们看下边这张图。

过滤器Filter是在请求进入容器后,但在进入servlet之前进行预处理,请求结束是在servlet处理完以后。
拦截器 Interceptor 是在请求进入servlet后,在进入Controller之前进行预处理的,Controller 中渲染了对应的视图之后请求结束。

4、拦截的请求范围不同

在上边我们已经同时配置了过滤器和拦截器,再建一个Controller接收请求测试一下。

 @Controller@RequestMapping()public class Test {@RequestMapping("/test1")@ResponseBodypublic String test1(String a) {System.out.println("我是controller");return null;}}

项目启动过程中发现,过滤器的init()方法,随着容器的启动进行了初始化。

此时浏览器发送请求,F12 看到居然有两个请求,一个是我们自定义的 Controller 请求,另一个是访问静态图标资源的请求。

看到控制台的打印日志如下:

执行顺序 :Filter 处理中 -> Interceptor 前置 -> 我是controller -> Interceptor 处理中 -> Interceptor 处理后

Filter 处理中

Interceptor 前置

Interceptor 处理中

Interceptor 后置

Filter 处理中

过滤器Filter执行了两次,拦截器Interceptor只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller中请求或访问static目录下的资源请求起作用。

5、注入Bean情况不同

在实际的业务场景中,应用到过滤器或拦截器,为处理业务逻辑难免会引入一些service服务。

下边我们分别在过滤器和拦截器中都注入service,看看有什么不同?

 @Componentpublic class TestServiceImpl implements TestService {@Overridepublic void a() {System.out.println("我是方法A");}}
过滤器中注入service,发起请求测试一下 ,日志正常打印出“我是方法A”。@Autowiredprivate TestService testService;​@Overridepublic void doFilter(ServletRequestservletRequest, ServletResponseservletResponse, FilterChainfilterChain) throwsIOException, ServletException {​System.out.println("Filter 处理中");testService.a();filterChain.doFilter(servletRequest, servletResponse);}

Filter 处理中

我是方法A

Interceptor 前置

我是controller

Interceptor 处理中

Interceptor 后置

在拦截器中注入service,发起请求测试一下 ,竟然TM的报错了,debug跟一下发现注入的service怎么是Null啊?

这是因为加载顺序导致的问题,拦截器加载的时间点在springcontext之前,而Bean又是由spring进行管理。

拦截器:老子今天要进洞房;

Spring:兄弟别闹,你媳妇我还没生出来呢!

解决方案也很简单,我们在注册拦截器之前,先将Interceptor 手动进行注入。注意:在registry.addInterceptor()注册的是getMyInterceptor() 实例。

 @Configurationpublic class MyMvcConfig implementsWebMvcConfigurer {​@Beanpublic MyInterceptorget MyInterceptor(){System.out.println("注入了MyInterceptor");returnnewMyInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistryregistry) {​registry.addInterceptor(getMyInterceptor()).addPathPatterns("/**");}}

6、控制执行顺序不同

实际开发过程中,会出现多个过滤器或拦截器同时存在的情况,不过,有时我们希望某个过滤器或拦截器能优先执行,就涉及到它们的执行顺序。

过滤器用@Order注解控制执行顺序,通过@Order控制过滤器的级别,值越小级别越高越先执行。

 @Order(Ordered.HIGHEST_PRECEDENCE)@Componentpublic class MyFilter2 implementsFilter {
//拦截器默认的执行顺序,就是它的注册顺序,也可以通过Order手动设置控制,值越小越先执行。@Overridepublic void addInterceptors(InterceptorRegistryregistry) {registry.addInterceptor(newMyInterceptor2()).addPathPatterns("/**").order(2);registry.addInterceptor(newMyInterceptor1()).addPathPatterns("/**").order(1);registry.addInterceptor(newMyInterceptor()).addPathPatterns("/**").order(3);}

看到输出结果发现,先声明的拦截器 preHandle() 方法先执行,而postHandle()方法反而会后执行。

postHandle() 方法被调用的顺序跟 preHandle() 居然是相反的!如果实际开发中严格要求执行顺序,那就需要特别注意这一点。

Interceptor1 前置

Interceptor2 前置

Interceptor 前置

我是controller

Interceptor 处理中

Interceptor2 处理中

Interceptor1 处理中

Interceptor 后置

Interceptor2 处理后

Interceptor1 处理后

拦截器和过滤器的同异相关推荐

  1. 区分Java拦截器和过滤器

    今天带大家分析java拦截器和过滤器的区别,文中有非常详细的解释说明,对正在学习java的小伙伴们有很好的帮助,需要的朋友可以参考下 一.过滤器(filter) 过滤器处于客户端与Web资源(Serv ...

  2. SpringBoot 拦截器和过滤器

    拦截器和过滤器 时光飞逝,最近也是很忙,但是忙到最后发现在自己并没有太多的成长 工作 学习 生活 没想到成长是不经意间的,像是被 推着,让你身不由己 午休时间,写写博客,也是保留一些自己的时间和空间 ...

  3. 拦截器和过滤器之间有很多相同之处,但是两者之间存在根本的差别

    转自:https://www.cnblogs.com/shangc/p/5939708.html 拦截器和过滤器之间有很多相同之处,但是两者之间存在根本的差别.其主要区别为以下几点: 1)拦截器是基于 ...

  4. struts2中拦截器和过滤器的比较

    拦截器和过滤器的区别: 1.拦截器是基于java的反射机制的,而过滤器是基于函数回调 2.过滤器依赖与servlet容器,而拦截器不依赖与servlet容器 3.拦截器只能对action请求起作用,而 ...

  5. 【SpringMVC】拦截器和过滤器

    拦截器: 拦截器是springmvc中的一种,需要实现HandlerInterceptor接口. 拦截器和过滤器类似,功能方向侧重点不同. 过滤器是用来过滤器请求参数,设置编码字符集等工作. 拦截器是 ...

  6. 关于SSM框架设置拦截器和过滤器

    我们知道拦截器和过滤器都是在项目中起到拦截过滤请求的功能,所以可能在设置的时候会傻傻分不清.这里我们先来比较它们的区别. 过滤器Filter是JavaEE标准,在Servlet的规范中定义的,是Ser ...

  7. 详解拦截器和过滤器的区别

    拦截器和过滤器的区别 过滤器和拦截器的区别: ①拦截器是基于java的反射机制的,而过滤器是基于函数回调. ②拦截器不依赖与servlet容器,过滤器依赖与servlet容器. ③拦截器只能对acti ...

  8. java 拦截器和过滤器的区别

    介绍 在 Java Web 应用程序中,拦截器和过滤器是两种不同的机制,用于在请求/响应处理过程中进行拦截和过滤.两者都可以用来在请求到达目标资源之前对其进行预处理.修改或拦截. 但是,拦截器和过滤器 ...

  9. 拦截器和过滤器有什么区别?

    首先了解什么是过滤器什么是拦截器 ①过滤器(Filter) 过滤器通过直接实现Filter接口实现,也可以通过@WebFilter注解实现特定的URL拦截 在Filter接口中定义了三个方法: ini ...

最新文章

  1. Oracle Exadata 简介
  2. Python培训分享:python如何用cookie实现自动模拟登录?
  3. appium: adb server is out of date.killing
  4. python3 pip 更换国内 pypi 镜像 源
  5. python类方法中使用:修饰符@staticmethod和@classmethod的作用与区别,还有装饰器@property的使用
  6. 红茶一杯话Binder(传输机制篇_下)
  7. 美国0封伊朗已经6天了,伊石油出口真归零了吗?
  8. android运行的线程中,android中线程是否运行在单独的进程中?
  9. 23种设计模式之责任链模式
  10. stack vs heap:栈区分配内存快还是堆区分配内存快 ?
  11. thymeleaf select_SpringBoot整合thymeleaf简单的CRUD
  12. 多个命令执行结果输出到同一个文件(批处理)
  13. change project compliance and jre to 1.5
  14. Mac 修改Hosts文件的方式
  15. 16qam星座图 matlab,16qam星形和矩形星座图调制解调matlab代码.doc
  16. 苹果电脑mac系统空间不足怎么清理内存优化?最详细的教程分享
  17. 6款好用的ppt制作软件推荐
  18. 计算机桌面颜色如何设置标准,教你把电脑屏幕设置成可以保护眼睛的颜色
  19. BZOJ 1189 HNOI2007 紧急疏散evacuate
  20. 【存储知识】文件系统与硬盘存储(分区、格式化、挂载、inode、软链接与硬链接)

热门文章

  1. 语音识别ASR和NLP有什么区别?
  2. [机器学习]概率图模型
  3. ResNet网络的改进版:ResNeXt
  4. 获取移动端ip的方法
  5. IDE Eval Reset 插件安装使用
  6. java计算机毕业设计计算机office课程平台MyBatis+系统+LW文档+源码+调试部署
  7. 深圳大学软工专硕数二英二改为数一英一,计算机专硕改为英一
  8. jQuery入门案例
  9. 如何保护Java程序 防止Java反编译
  10. 使用JMETER进行REST API测试