拦截器就是进来拦截一次,出去拦截一次。过滤器就是进来,通过了,出去就走另一条路了。
拦截器一定在Controller之前执行,就像Filter一定在Servlet之前执行
我们形象的比喻一下到达Controller和Servelt会发出咚的一声,然后这是有三个拦截器,有三个过滤器。过滤器的执行步骤是:过滤器1—>过滤器2—>过滤器3—>咚,而拦截器的执行步骤是:拦截器1—>拦截器2—>拦截器3—>咚—>拦截器3—>拦截器2—>拦截器1
拦截器属于DispatcherServlet之后的过程,
正常的一个请求发送到后端会先到达web.xml,然后发给了Filter,然后交给DispatcherServlet,接着到达Interceptor…(可能会有很多个拦截器),然后才到达Controller,处理完了以后在返回给DispatcherServlet,然后直接返回给Tomcat
SpringMVC执行原理中Handler是两种东西,有可能是Controller,也有可能是Interceptor。他们是一个响应链,请求先到Interceptor1–>Interceptor2–>Interceptor3–>Controller–>Interceptor3–>Interceptor2–>Interceptor1(1 2 3 咚 3 2 1)
拦截器可以将过滤器的活给做了
过滤器Filter依赖于Servlet容器,要在web.xml文件中配置,拦截器Interceptor依赖于web框架,他要在SpringMVC配置文件中配置,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。但是缺点是只能对controller请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
过滤器的触发时机是容器后,servlet之前,所以过滤器的doFilter(
ServletRequest request, ServletResponse response, FilterChain chain)的入参是ServletRequest ,而不是httpservletrequest。因为过滤器是在httpservlet之前。
实现一个拦截器
public class MyInterceptor implements HandlerInterceptor {
@Override
//前置判断,决定这个请求到达能不能执行。这就相当于Controller层的"AOP"过程
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("前置开始");
return true;
}
@Override
//因为是Controller返回的ModelAndView,也就意味着这个方法是在Controller执行完了以后,再回到这里,修改ModelAndView
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("处理完成 ");
}
@Override
//最终完成后执行它
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("全部结束");
}
}
要让这个Interceptor生效就要在容器中配置他,让他进入到SpingMVC体系下
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<ref bean="myInterceptor">ref>
mvc:interceptor>
mvc:interceptors>
<bean id="myInterceptor" class="com.lanou.controller.MyInterceptor">bean>
现在实现多个拦截器,这次我们自定义的拦截器继承HandlerInterceptorAdaptor(HandlerInterceptor接口的实现类)如果实现的是接口那接口中的方法都要重写(虽然有的方法是default的,但是最好还是都要实实现的),但如果继承实现类,就可以挑选方法重写了拦截器执行顺序即配置顺序
public class MySecondInterceptor extends HandlerInterceptorAdapter {
public MySecondInterceptor() {
super();
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("second 前置");
return super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("second 后置");
super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("second 结束");
super.afterCompletion(request, response, handler, ex);
}
@Override
//在分布式中处理最终事务一致性可能会用到
//没有并发处理请求就不会走这里
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("second concurrent");
super.afterConcurrentHandlingStarted(request, response, handler);
}
}
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<ref bean="myInterceptor">ref>
mvc:interceptor>
<bean id="secondInterceptor" class="com.lanou.controller.MySecondInterceptor">bean>
mvc:interceptors>
<bean id="myInterceptor" class="com.lanou.controller.MyInterceptor">bean>
发现其实拦截器真正执行的顺序是 1 2 3 咚 3 2 1 3 2 1(1 2 3表示拦截器1,2,3)
如果调换两个拦截器配置的位置,执行结果也就调换
<mvc:interceptors>
<bean id="secondInterceptor" class="com.lanou.controller.MySecondInterceptor">bean>
<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<ref bean="myInterceptor">ref>
mvc:interceptor>
mvc:interceptors>
<bean id="myInterceptor" class="com.lanou.controller.MyInterceptor">bean>
有关Interceptor接口的preHandle方法的handler属性的操作
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//这里的这个handler就是告诉我们这个请求将要找哪个方法
HandlerMethod method = (HandlerMethod) handler;
System.out.println(method.getMethod().getName());//访问login.do,即要执行login方法。结果打印出 login
System.out.println(method.getBean().getClass().getName());//getBean():获得方法的所属对象
//知道了方法所属对象,知道了参数,就能通过反射执行这个方法
method.getMethod().invoke(method.getBean(),method.getMethodParameters());//这里就可以对这个方法来执行更多的操作
//但是由于我们是非侵入编程,最终只要让preHandle得到两个结果,要么执行,要么不执行,这就行了。
return true;
}
}
一般用preHandle方法的前两个参数来判断传进来的参数的合法性的,用后面的那个参数来判断用户状态的。
postHandle方法里的ModelAndView参数,如果后端要向前端然后一个ModelAndView,那么这里可以截获他,然后修改他再返回给前端。
过滤器
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("before...");
chain.doFilter(request, response);
System.out.println("after...");
}
拦截器
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
}
preHandle()
这个方法是在过滤器的chain.doFilter(request, response)
方法的前一步执行,也就是在 System.out.println("before...")
chain.doFilter(request, response)
之间执行。afterCompletion()
方法是在过滤器返回给前端前一步执行,也就是在chain.doFilter(request, response)
System.out.println("after...")
之间执行。学习参考:https://www.cnblogs.com/panxuejun/p/7715917.html