Spring MVC - 拦截器

1. 拦截器概述

   SpringMVC 中的拦截器似于 Servlet 中的过滤器,它主要作用是在用户请求到达控制器之前或之后进行拦截,可以对请求或响应进行一些自定义处理。例如通过拦截器可以进行用户登录验证、用户权限验证、记录请求信息的日志、修改响应信息等等。

   拦截器将按照一定的顺序联结成一条链,这条链称为拦截链(Interceptor Chain)。在访问被拦截的方法,或者字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。

   拦截器就是AOP思想的具体体现。

 拦截器与过滤器的区别

   过滤器注重在请求和响应的流程中进行处理,可以修改请求和响应内容。例:设置编码格式、请求头、状态码等等操作。

   拦截器注重对于控制器进行前置和后置处理,在请求到达控制器之前或之后的特定操作。例:打印日志、权限验证等等操作。

   使用范围:

  1. 过滤器是 servlet 规范的一部分,任何javaweb工程都可以使用
  2. 拦截器是 springmvc 框架自己的,只有使用了springmvc框架的工程才能使用

  拦截范围:

  1. 过滤器在"url-pattern"中配置"/*"之后可以对所有要访问的资源拦截
  2. 拦截器只会拦截访问的控制器方法,如果访问的是 jsp\html\css\image或者js等静态资源,拦截器不会进行拦截

 fillter、servlet、interceptor、controller执行顺序

2. 拦截器自定义

拦截器中的三个方法:

   preHandle:控制器方法执行之前执行preHandle(),其boolean类型的返回值表示是否拦截,返回true为放行,即调用控制器方法;返回false表示拦截,即不调用控制器方法。

   postHandle:控制器方法执行之后执行postHandle()。

   afterCompletion:处理完视图和模型数据,渲染视图完毕之后执行afterComplation()。

2.1 拦截器创建

拦截器创建有两种方式,分别是通过实现 HandlerInterceptor接口 和实现 WebRequestInterceptor接口 两种方式。

public interface HandlerInterceptor {
    default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  return true;  }

    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 {     }
}

public interface WebRequestInterceptor {
    void preHandle(WebRequest request) throws Exception;

    void postHandle(WebRequest request, @Nullable ModelMap model) throws Exception;

    void afterCompletion(WebRequest request, @Nullable Exception ex) throws Exception;
}

2.1.1 HandlerInterceptor

通过 HandlerInterceptor 接口创建自定义拦截器,这三个方法可以选择性重写,因为在接口中它的修饰符是  default。

@Slf4j
@Component
public class HandlerInterceptorDemo implements HandlerInterceptor {

    /**
     * 原始方法调用前执行的内容
     * 返回值类型可以拦截控制的执行,true放行,false终止
     * @param request  请求信息
     * @param response 响应信息
     * @param handler 拦截器对象
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //return HandlerInterceptor.super.preHandle(request, response, handler);
        log.info("原始方法调用前执行的内容 - request.getContextPath(): {}",request.getContextPath());
        log.info("原始方法调用前执行的内容 - request.getMethod(): {}",request.getMethod());
        return true;
    }

    /**
     * 原始方法调用后执行的内容
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("原始方法调用后执行的内容 - modelAndView: {}",modelAndView);
        //HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    /**
     * 原始方法调用完成后执行的内容
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("原始方法调用完成后执行的内容:");
        //HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

2.1.2 WebRequestInterceptor

通过 WebRequestInterceptor 接口创建自定义拦截器,这三个方法必须全部重写。

@Slf4j
@Component
public class WebRequestInterceptorDemo implements WebRequestInterceptor {

    /**
     * 原始方法调用前执行的内容
     * @param request
     * @throws Exception
     */
    @Override
    public void preHandle(WebRequest request) throws Exception {
        log.info("原始方法调用前执行的内容 - request.getContextPath(): {}",request.getContextPath());
    }

    /**
     * 原始方法调用后执行的内容
     * @param request
     * @param model
     * @throws Exception
     */
    @Override
    public void postHandle(WebRequest request, ModelMap model) throws Exception {
        log.info("原始方法调用后执行的内容 - ModelMap: {}",model);
    }

    /**
     * 原始方法调用完成后执行的内容
     * @param request
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(WebRequest request, Exception ex) throws Exception {
        log.info("原始方法调用完成后执行的内容:");
    }
}

2.2 拦截器配置 

2.2.1 XML配置

springmvc.xml配置方式。

    <mvc:interceptors>
        <!--基本配置 第一种方式: 在拦截器类上不需要添加 @Component注解, 注意基本配置默认拦截所有请求-->
        <bean class="com.app.library.interceptor.HandlerInterceptorDemo"></bean>
        <!--基本配置 第二种方式: 在拦截器类上添加 @Component注解,并在配置springmvc.xml文件中开启注解驱动-->
        <ref bean="webRequestInterceptorDemo"/>
        
        <!--高级配置:可以针对性拦截某些路径,某些路径进行放行-->
        <mvc:interceptor>
            <!--拦截所有路径-->
            <mvc:mapping path="/**"/>
            <!--放行 /test/ 路径的处理器 -->
            <mvc:exclude-mapping path="/test/"/>
            <!--指定使用那个拦截器-->
            <ref bean="HandlerInterceptorDemo"/>
        </mvc:interceptor>
    </mvc:interceptors>

2.2.2 配置类配置

@Configuration 注解是 Spring3.0 之后提供的注解。 

@Configuration  //标注这是一个配置类
@EnableWebMvc   //启动webmvc配置
@ComponentScan("com.app.library.interceptor.controller")  //根据路径扫描包下的处理器,并进行过拦截
public class InterceptorConfig implements WebMvcConfigurer { //实现WebMvcConfigurer接口配置拦截器信息可以简化开发,但具有一定的侵入性。

    @Autowired
    private HandlerInterceptorDemo Interceptor;
    //@Autowired
    //private WebRequestInterceptorDemo Interceptor;

    /** 将拦截器与拦截路径进行绑定  */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(Interceptor).addPathPatterns("/inter/*");
                // "/**" 拦截全部请求
                // "/inter/*" 拦截这个请求路径下的所有请求
                // "/inter/handle" 拦截指定路径的请求       
    }
}

3. 拦截器执行流程

3.1 拦截器执行顺序

    public class DispatcherServlet extends FrameworkServlet {
        //DispatcherServlet 核心方法
        protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
            //1 applyPreHandle方法执行 这里会根据preHandle()的返回值进行判断是否执行 ,ture-执行处理器方法..  false-直接return,不向下走
            if(!mappedHandler.applyPreHandle(processedRequest, response)){ return; }
            //2 处理器方法执行
            mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
            //3 applyPostHandle方法执行 
            mappedHandler.applyPostHandle(processedRequest, response, mv);
            //4 页面渲染 + afterCompletion方法执行
            processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
        }
        private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception {
            //4.1 页面渲染
            render(mv, request, response);
            //4.2 triggerAfterCompletion方法执行
            mappedHandler.triggerAfterCompletion(request, response, (Exception)null);
        }
    }

    public class HandlerExecutionChain {
        //拦截器集合 
        private final List<HandlerInterceptor> interceptorList;
        //集合角标
        private int interceptorIndex;
        
        //1.1 执行 applyPreHandle
        boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
            // 正序 遍历拦截器List集合
            for(int i = 0; i < this.interceptorList.size(); this.interceptorIndex = i++) { 
                //获取拦截器对象
                HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
                //执行preHandle方法,并判断preHandle方法返回值 preHandle方法返回true-不走下面代码块 返回false-执行代码块
                if (!interceptor.preHandle(request, response, this.handler)) {
                    //当 preHandle() = false 执行 triggerAfterCompletion()
                    this.triggerAfterCompletion(request, response, (Exception)null);
                    //返回false 
                    return false;
                }
            }
            return true;
        }

        //3.1 执行applyPostHandle
        void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception {
            // 倒序 遍历拦截器List集合
            for(int i = this.interceptorList.size() - 1; i >= 0; --i) {
                //获取拦截器对象
                HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
                //执行拦截器 postHandle方法
                interceptor.postHandle(request, response, this.handler, mv);
            }
        }

        //4.2.1 triggerAfterCompletion方法执行
        void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
            //按照 拦截器集合当时最大脚标 进行 倒序 遍历拦截器集合
            for(int i = this.interceptorIndex; i >= 0; --i) {
                //获取拦截器对象
                HandlerInterceptor interceptor = (HandlerInterceptor)this.interceptorList.get(i);
                try {
                    //执行 afterCompletion方法
                    interceptor.afterCompletion(request, response, this.handler, ex);
                } catch (Throwable var7) {
                    logger.error("HandlerInterceptor.afterCompletion threw exception", var7);
                }
            }

        }
    }

3.2 多个拦截器的执行顺序

状况一:若每个拦截器的preHandle()都返回true

   preHandle按照配置文件中的顺序执行;handle方法全部执行,postHandle按照配置文件中的反序执行;afterCompletion按照配置文件中的反序执行。

状况二:若拦截链中某拦截器的preHandle()返回了false

   该拦截器之前的所有preHandle()方法都执行,该拦截器的preHandle()方法也会执行。 handle方法不会执行,所有拦截器的postHandle()方法都不执行,该拦截器之前的afterCompletion()方法,逆序执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值