SpringMVC中的拦截器

Java知识点总结:想看的可以从这里进入

2.2、拦截器

2.2.1、简介

在 DispatcherServlet 中 映射器 HandlerMapping 会找到一个Handler和与之对应的拦截器组成一个执行链返回给DispatcherServlet,并且在 Handler 执行前后,调用拦截器的一些方法进行处理。

image-20230303140651479

这个拦截器就是Spring MVC 提供的一种强大的功能组件,可以在请求进入控制前、控制器处理完请求后、渲染完视图后、可以执行的一些额外操作。类似于Servlet中的过滤器,可以做一些权限认证、记录请求日志、判断用户状态等等一些操作。(比方说很常见的一个功能:判断用户是否登录,没有登录不让操作,会自动跳转到登录界面。这个在很多网站都会有,此功能就能通过拦截器实现,登录时将用户登录信息保存到session中,在进行其他操作时,在拦截器中判断session中是否有用户信息,如果没有就跳转到登录界面,如果有就放行)。

Spring MVC 中有一个包: org.springframework.web.servlet ,包中提供了一个 HandlerInterceptor 接口,该接口包含 3 个方法,这个接口就是拦截器的顶层接口。

public interface HandlerInterceptor {
    //该方法在控制器方法之前执行,其返回值用来表示是否中断后续操作。
    //返回值为 true 时,表示继续向下执行;
	//返回值为 false 时,表示中断后续的操作
	default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		return true;
	}
    //在控制器方法调用之后,解析式图之前执行。我们可以通过此方法对请求域中的模型(Model)数据和视图做出进一步的修改。
	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 {
	}

}

通过这三个方法我们就能确定拦截器的执行流程了:

image-20220916114403494

2.2.2、开发使用

拦截器必须实现 HandlerInterceptor接口,它内部的三个方法都是default修饰的,所以可以随意重写其中的方法。

public class MyHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法执行:"+request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法执行"+request.getRequestURI());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法执行"+request.getRequestURI());
    }
}

实现HandlerInterceptor后如果需要让它拦截Handler,还需要在SprinMVC的XML配置文件中进行配置,才会使其生效。标签为:mvc:interceptors,它内部有三种字标签,用于拦截器的配置

一级子标签二级子标签说明
bean用于定义一个全局拦截器,对所有的请求进行拦截。
ref引入一个外部定义的Bean作为全局拦截器。需要配和bean 标签使用
mvc:interceptor按从上到下的顺序依次配置:
mvc:mapping:用来配置拦截器拦截的路径
mvc:exclude-mapping:来配置不需要被拦截器拦截的路径。
bean :只拦截指定的路径,非全局
定义一个指定拦截路径的拦截器。

根据子标签的不同含义,可以配置不同范围的拦截器,需要合理的使用:

  1. 配置MyHandlerInterceptor为全局拦截器(使用Bean)

    <!--配置拦截器-->
    <mvc:interceptors>
        <!--这个拦截器是全局拦截器拦截全部请求-->
        <bean class="com.yu.interceptor.MyHandlerInterceptor"/>	
        
        <!--如果还有其他的拦截器,可以继续配置 -->
    </mvc:interceptors>
    

    image-20220916130901875

  2. 配置MyHandlerInterceptor为全局拦截器(使用ref)

    <bean id="interceptor" class="com.yu.interceptor.MyHandlerInterceptor"/>
    <!--配置拦截器-->
    <mvc:interceptors>
        <!--这个引入的拦截器也是全局的,拦截全部请求-->
        <ref bean="interceptor"/>	
        
        <!--如果还有其他的拦截器,可以继续配置 -->
    </mvc:interceptors>
    

    image-20220916130756427

  3. 配置非全局拦截器

    <!--配置拦截器 -->
    <mvc:interceptors>
        <mvc:interceptor>
            <!-- 配置拦截器拦截的请求路径,'/**' 是拦截所有的-->
            <mvc:mapping path="/**"/>
            <!--配置拦截器不需要拦截的请求路径-->
            <mvc:exclude-mapping path="/index"/>
            <mvc:exclude-mapping path="/disposeParams/testJson"/>
            <!--定义在mvc:interceptor内的拦截器,非全局,只能拦截mvc:interceptor中规定的路径-->
            <bean class="com.yu.interceptor.MyHandlerInterceptor"></bean>
        </mvc:interceptor>
        
        <!--如果还有其他的拦截器,可以继续配置 -->
    </mvc:interceptors>
    
    image-20220916130658775
2.2.3、多拦截器机制

拦截器通常不会只设置一个,如果配置了多个拦截器,这些拦截器是有先后顺序的,我们先定义两个拦截器,看看他们是怎么执行的

public class MyHandlerInterceptor1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("第一个拦截器preHandle方法执行:"+request.getRequestURI());
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("第一个拦截器postHandle方法执行"+request.getRequestURI());
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("第一个拦截器afterCompletion方法执行"+request.getRequestURI());
    }
}
public class MyHandlerInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("第二个拦截器preHandle方法执行:"+request.getRequestURI());
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("第二个拦截器postHandle方法执行"+request.getRequestURI());
    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("第二个拦截器afterCompletion方法执行"+request.getRequestURI());
    }
}

将两个拦截器在XML文件中进行配置:

<!--配置拦截器-->
<mvc:interceptors>
    <!--拦截路径-->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/disposeParams/testJson"/>
        <bean class="com.yu.interceptor.MyHandlerInterceptor1"/>
    </mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <mvc:exclude-mapping path="/disposeParams/testJson"/>
        <bean class="com.yu.interceptor.MyHandlerInterceptor2"/>
    </mvc:interceptor>
</mvc:interceptors>

启动后,执行一个方法看拦截器的执行顺序:

image-20230303145433369

从上面可以总结出来多个拦截器的执行顺序:

image-20220916132520025

如果preHandle前置方法返回的false,那么要根据它的所在拦截器的位置进行分析:

  • 如果返回 false 的拦截器是第一个拦截器,执行完第一个拦截器的preHandle方法后就直接结束

    //将第一个拦截器的preHandle方法的返回值改成false,第二个拦截器还是为true
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("第一个拦截器preHandle方法执行:"+request.getRequestURI());
        return false;
    } 
    

    只执行了第一个preHandle方法后,就直接结束了,后面的没有执行

    image-20230303145919956

    image-20230303150139307
  • 如果返回 false 的拦截器是不第一个拦截器

    • 它和它前面的拦截器的 preHandle方法都执行,后面拦截器的不执行
    • 所有拦截器的 postHandle 方法都不执行
    • 它前面的所有拦截器的 afterComplation 方法都执行,后面拦截器的不执行
    //将第一个拦截器的preHandle方法的返回值改成true,第二个拦截器的改为false
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("第二个拦截器preHandle方法执行:"+request.getRequestURI());
        return false;
    }
    

    第一个拦截和第二个拦截器的preHandle方法都执行了,但是所有的postHandle 都没有执行,而第一个拦截器的 afterComplation 方法执行了。

    image-20230303150232966 image-20230303150139307
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰 羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值