0


【SpringMVC】Filter过滤器、AOP切面类、Interceptors拦截器各自的执行顺序

文章内容:探究Filter过滤器、AOP切面类、interceptors,这几个类都有一个特性“拦截”(拦截器和过滤器实现的都是拦截功能,切面类是实现在某部分代码前执行特定代码,例如登录前要求用户验证)


提示:本文不深究Filter过滤器、AOP切面类、interceptors的原理,解释采用通俗易懂的方式进行解释,后续将针对三者写出详细的文章
点击跳转:AOP切面类详解


  • 原生的Filter:可以实现以下功能- 调用目标资源之前,让一段代码执行。- 是否调用目标资源(即是否让用户访问web资源)。- 调用目标资源之后,让一段代码执行。

Filter(过滤器) 的基本功能是对 Servlet 容器调用 Servlet (JSP)的过程进行拦截, 从而在 Servlet 处理请求前和 Servlet 响应请求后实现一些特殊的功能。

即在调用某个资源之前,能进行拦截


  • AOP切面类: - 需要在核心业务前执行该辅助功能- 需要在核心业务执行之后执行该辅助功能- 需要在报错时候执行该辅助功能更- 需要在返回结果是执行该辅助功能- 需要在方法执行之前之后异常时执行该辅助功能

用动态代理来实现在执行某部分代码之前执行特定的代码
点击跳转:AOP切面类详解

需要注意:
返回通知(after-returning):当核心业务代码执行完成后执行,发生异常不执行
后置通知(after):不论目标方法是否发生异常都会执行,若无异常,则执行顺序在返回通知之后


  • Interceptor:拦截器: 属于SpringMVC,实现的功能和过滤器一样,但是也有一些不同点 - 工作平台不同: - 过滤器工作在 Servlet 容器中- 拦截器工作在 SpringMVC 的基础上- 拦截范围不同: - 过滤器:能够拦截到的最大范围是整个 Web 应用- 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求- IOC容器的支持: - 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的- 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

实现的功能和过滤器一样,开发使用的时候,假如可以用拦截器实现,就可以不用过滤器了



下面来研究,在存在多个过滤类/存在多个切面类/存在多个拦截器,其执行顺序

过滤器

1.准备工作:
准备两个过滤器(构成过滤器链),配置

publicclassFilter01implementsFilter{@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{Filter.super.init(filterConfig);System.out.println("Filter01:初始化");}@OverridepublicvoiddoFilter(ServletRequest request,ServletResponse response,FilterChain chain)throwsIOException,ServletException{System.out.println("Filter01:过滤");
        chain.doFilter(request, response);}@Overridepublicvoiddestroy(){Filter.super.destroy();System.out.println("Filter01:过滤器销毁");}}
publicclassFilter02implementsFilter{@Overridepublicvoidinit(FilterConfig filterConfig)throwsServletException{Filter.super.init(filterConfig);System.out.println("Filter02:初始化");}@OverridepublicvoiddoFilter(ServletRequest request,ServletResponse response,FilterChain chain)throwsIOException,ServletException{System.out.println("Filter02:过滤");
        chain.doFilter(request,response);}@Overridepublicvoiddestroy(){Filter.super.destroy();System.out.println("Filter02:过滤器销毁");}}
<?xml version="1.0" encoding="UTF-8"?><web-appxmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><filter><filter-name>Filter01</filter-name><filter-class>com.example.demo_web.filter.Filter01</filter-class></filter><filter><filter-name>Filter02</filter-name><filter-class>com.example.demo_web.filter.Filter02</filter-class></filter><filter-mapping><filter-name>Filter02</filter-name><url-pattern>/TestServlet</url-pattern></filter-mapping><filter-mapping><filter-name>Filter01</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>

执行顺序:

Filter02:初始化
Filter01:初始化
[2022-02-03 04:31:29,250] Artifact demo_web:war exploded: Artifact is deployed successfully
[2022-02-03 04:31:29,250] Artifact demo_web:war exploded: Deploy took 991 milliseconds
Filter02:过滤
Filter01:过滤

执行顺序:过滤器链执行顺序和设置的filter-mapping先后顺序有关,先设置先过滤,和filter标签顺序无关
注意:使用多个过滤器时,构成过滤器链,假如在dofilter方法中如果没有 chain.doFilter(request,response);那么请求在进入第一个过滤器后无法通过



切面类

1.准备工作
两个切面类

@Aspect@ComponentpublicclassTestAOP2{@Before(value ="execution(public int com.Calc.add(int ,int ))")publicvoidprintLogBefore(JoinPoint joinPoint){System.out.println("TEST2:Before");}@AfterReturning(
            value ="execution(public int com.Calc.add(int ,int ))",
            returning ="returnValue"//定义的形参名字)publicvoidprintLogAfterSuccess(JoinPoint joinPoint,Object returnValue){System.out.println("TEST2:AfterReturning");}@AfterThrowing(
            value ="execution(public int com.Calc.add(int ,int ))",
            throwing ="targetException")publicvoidprintLogAfterException(JoinPoint joinPoint,Throwable targetException){System.out.println("TEST2:printLogAfterException");}@After("execution(public int com.Calc.add(int ,int ))")publicvoidprintLogFinish(){System.out.println("TEST2:After");}}
@Aspect@ComponentpublicclassTestAOP3{@Before(value ="execution(public int com.Calc.add(int ,int ))")publicvoidprintLogBefore(JoinPoint joinPoint){System.out.println("TEST3:Before");}@AfterReturning(
            value ="execution(public int com.Calc.add(int ,int ))",
            returning ="returnValue")publicvoidprintLogAfterSuccess(JoinPoint joinPoint,Object returnValue){System.out.println("TEST3:AfterReturning");}@AfterThrowing(
            value ="execution(public int com.Calc.add(int ,int ))",
            throwing ="targetException")publicvoidprintLogAfterException(JoinPoint joinPoint,Throwable targetException){System.out.println("TEST3:printLogAfterException");}@After("execution(public int com.Calc.add(int ,int ))")publicvoidprintLogFinish(){System.out.println("TEST3:After");}}

无抛错情况下:

TEST2:Before
TEST3:Before
TEST3:AfterReturning
TEST3:After
TEST2:AfterReturning
TEST2:After

有抛错

TEST2:Before
TEST3:Before
TEST3:AfterReturning
TEST3:After
TEST2:AfterReturning
TEST2:After

执行顺序:
Spring 官方文档中是这样描述的:当在不同切面定义的两条相同类型通知都需要在同一连接点上运行时,除非另行指定,否则执行顺序是不确定的。 您可以通过指定优先级来控制执行顺序。 通过在切面类中实现 org.springframework.core.Ordered 接口或使用 @Order 注解对其进行注解。 给定两个切面,从 Ordered.getValue()(或 @Order 注解值)返回较低值的切面具有较高的优先级。
不过针对上面的描述,亲测,在不设定优先级的情况下,两个切面类执行顺序和其类名的排序有关,看下图,TestAOP2排序先于TestAOP3,TestAOP2就先执行

Test2和Test3,记住他们的类名排序顺序和结果
在这里插入图片描述

TEST2:Before
TEST3:Before
TEST3:AfterReturning
TEST3:After
TEST2:AfterReturning
TEST2:After

现在我们把Test3命名为Test1
在这里插入图片描述
执行结果

TEST1:Before
TEST2:Before
TEST2:AfterReturning
TEST2:After
TEST1:AfterReturning
TEST1:After


拦截器

1.准备工作:
两个拦截器,然后在spring-mvc文件中配置拦截器

<mvc:interceptors><beanclass="com.mvc.interceptor.Interceptor01"/><beanclass="com.mvc.interceptor.Interceptor02"/></mvc:interceptors>
publicclassInterceptor01implementsHandlerInterceptor{privateLogger logger=LoggerFactory.getLogger(this.getClass());@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{
        logger.debug("Interceptor01:preHandle");returntrue;}@OverridepublicvoidpostHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView)throwsException{
        logger.debug("Interceptor01:postHandle");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@OverridepublicvoidafterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex)throwsException{
        logger.debug("Interceptor01:afterCompletion");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}
publicclassInterceptor02implementsHandlerInterceptor{privateLogger logger=LoggerFactory.getLogger(this.getClass());@OverridepublicvoidafterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex)throwsException{
        logger.debug("Interceptor02:afterCompletion");HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}@OverridepublicvoidpostHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView)throwsException{
        logger.debug("Interceptor02:postHandle");HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@OverridepublicbooleanpreHandle(HttpServletRequest request,HttpServletResponse response,Object handler)throwsException{
        logger.debug("Interceptor02:preHandle");returntrue;}}

2.测试:

[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor01 - Interceptor01:preHandle
[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor02 - Interceptor02:preHandle
[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor02 - Interceptor02:postHandle
[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor01 - Interceptor01:postHandle
[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor02 - Interceptor02:afterCompletion
[http-nio-8080-exec-21] DEBUG com.mvc.interceptor.Interceptor01 - Interceptor01:afterCompletion

两个拦截器的顺序执行,其实是比较难记下来的。

  • preHandle()方法:和配置的顺序一样
  • 目标handler方法
  • postHandle()方法:和配置的顺序相反
  • 渲染视图
  • afterCompletion()方法:和配置的顺序相反
标签: java 后端

本文转载自: https://blog.csdn.net/m0_52410356/article/details/122776212
版权归原作者 写Bug的渣渣高 所有, 如有侵权,请联系我们删除。

“【SpringMVC】Filter过滤器、AOP切面类、Interceptors拦截器各自的执行顺序”的评论:

还没有评论