springboot学习13——springmvc拦截器解析

八、拦截器

当请求来到DispatcherServlet时,它会根据HandlerMapping的机制找到处理器,这样就会返回一个HandlerExecutionChain对象,这个对象包含处理器和拦截器。
这里的拦截器会对处理器进行拦截,这样通过拦截器就可以增强处理器的功能。

1.拦截器的设计
首先所有的拦截器都需要实现HandlerInterceptor接口。

HandlerInterceptor源码

package org.springframework.web.servlet;

/**** imports ****/
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 {
    }

}

springboot学习13——springmvc拦截器解析_第1张图片

拦截器执行过程:
•执行preHandle方法,该方法会返回一个布尔值。如果为false,则结束所有流程;如果为true,则执行下一步。
•执行处理器逻辑,它包含控制器的功能。
•执行postHandle方法。
•执行视图解析和视图渲染。
•执行afterCompletion方法。
因为这个接口是Java 8的接口,所以3个方法都被声明为default,并且提供了空实现。当我们需要自己定义方法的时候,只需要实现HandlerInterceptor,覆盖其对应的方法即可。

2.开发拦截器
自定义简单拦截器

/******** 拦截器1********/ 
package com.springboot.chapter10.interceptor;
/**** imports ****/
public class MulitiInterceptor1 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler)
            throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】处理器前方法");
        // 返回true,不会拦截后续的处理
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】处理器后方法");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, 
            HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("【" + this.getClass().getSimpleName()
            +"】处理器完成方法");
    }
}

这里的代码实现了HandlerInterceptor,然后按照自己的需要重写了3个具体的拦截器方法。
有了这个拦截器,Spring MVC并不会发现它,它还需要进行注册才能够拦截处理器,为此需要在配置文件中实现WebMvcConfigurer接口,最后覆盖其addInterceptors方法进行注册拦截器。

注册拦截器

package com.springboot.chapter10.main;
/**** imports ****/
// 声明配置类
@Configuration
// 定制扫描路径
@SpringBootApplication(scanBasePackages = "com.springboot.chapter10")
/****其他注解****/
public class Chapter10Application implements WebMvcConfigurer {
    public static void main(String[] args) {
        SpringApplication.run(Chpter10Application.class, args);
    }
    ......
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器到Spring MVC机制,然后它会返回一个拦截器注册
        InterceptorRegistration ir = registry.addInterceptor(new Interceptor1());
        // 指定拦截匹配模式,限制拦截器拦截请求
        ir.addPathPatterns("/interceptor/*");
}
}

这里通过实现WebMvcConfigurer接口,重写其中的addInterceptors方法,进而加入自定义拦截器——Interceptor1,然后指定其拦截的模式,所以它只会拦截与正则式“/interceptor/*”匹配的请求。

拦截控制器

package com.springboot.chapter10.controller;
/**** imports ****/
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {
    @GetMapping("/start")
    public String start() {
        System.out.println("执行处理器逻辑");
        return "/welcome";
    }
}

这里的控制器的start方法只是打开一个欢迎页面,十分简单,同时它定义了拦截“/interceptor/start”,而这个请求显然会被所创建的拦截器所拦截,所以只需要请求这个方法,请求就会被我们的拦截器拦截。
为了更好地测试这个拦截器,我们在欢迎页面也打印一下后台的信息,这个页面如代码清单10-38所示。

测试:http://localhost:8080/interceptor/start
后台打印的日志:

【MulitiInterceptor1】处理器前方法
执行处理器逻辑
【MulitiInterceptor1】处理器后方法
【MulitiInterceptor1】处理器完成方法

显然处理器被拦截器拦截了,这里需要注意的是拦截器方法的执行顺序。有兴趣的读者可以把拦截器的preHandle方法返回修改为false,或者让控制器抛出异常,然后重新测试,从而进一步掌握整个拦截器的流程。

3.多个拦截器的顺序
定义多个拦截器
拦截器2 ,拦截器3除类名外,内容同拦截器1。

注册多个拦截器

@Override
public void addInterceptors(InterceptorRegistry registry) {
    // 注册拦截器到Spring MVC机制中
    InterceptorRegistration ir 
        = registry.addInterceptor(new MulitiInterceptor1());
    // 指定拦截匹配模式
    ir.addPathPatterns("/interceptor/*");
    // 注册拦截器到Spring MVC机制中
    InterceptorRegistration ir2 = registry.addInterceptor(new MulitiInterceptor2());
    // 指定拦截匹配模式
    ir2.addPathPatterns("/interceptor/*");
    // 注册拦截器到Spring MVC机制中
    InterceptorRegistration ir3 = registry.addInterceptor(new MulitiInterceptor3());
    // 指定拦截匹配模式
    ir3.addPathPatterns("/interceptor/*");
}

这样这些拦截器都会拦截与"/interceptor/*"匹配的请求。这里使用浏览器再次请求代码清单10-37中的start方法,于是可以看到如下的日志打印出来:

【MulitiInterceptor1】处理器前方法
【MulitiInterceptor2】处理器前方法
【MulitiInterceptor3】处理器前方法
执行处理器逻辑
【MulitiInterceptor3】处理器后方法
【MulitiInterceptor2】处理器后方法
【MulitiInterceptor1】处理器后方法
【MulitiInterceptor3】处理器完成方法
【MulitiInterceptor2】处理器完成方法
【MulitiInterceptor1】处理器完成方法

这个结果是责任链模式的规则,对于处理器前方法采用先注册先执行,而处理器后方法和完成方法则是先注册后执行的规则。

只是上述仅测试了处理器前(preHandle)方法返回为true的场景,在某些时候还可能返回为false,这个时候又如何呢?为此,可以将MulitiInterceptor2的preHandle方法修改返回为false,
然后再进行测试,其日志如下:
【MulitiInterceptor1】处理器前方法
【MulitiInterceptor2】处理器前方法
【MulitiInterceptor1】处理器完成方法
从上面的日志可以看出,处理器前(preHandle)方法会执行,但是一旦返回false,则后续的拦截器、处理器和所有拦截器的处理器后(postHandle)方法都不会被执行。
完成方法afterCompletion则不一样,它只会执行返回true的拦截器的完成方法,而且顺序是先注册后执行。

九、Spring MVC拾遗

@ResponseBody转换为JSON的秘密

一直以来,当想把某个控制器的返回转变为JSON数据集时,只需要在方法上标注@ResponseBody注解即可,那么Spring MVC是如何做到的呢?

在进入控制器方法前,当遇到标注的@ResponseBody后,处理器就会记录这个方法的响应类型为JSON数据集。当执行完控制器返回后,处理器会启用结果解析器(ResultResolver)去解析这个结果,
它会去轮询注册给Spring MVC的HttpMessageConverter接口的实现类。
因为MappingJackson2HttpMessageConverter这个实现类已经被Spring MVC所注册,加上Spring MVC将控制器的结果类型标明为JSON,所以就匹配上了,于是通过它就在处理器内部把结果转换为了JSON。
当然有时候会轮询不到匹配的HttpMessageConverter,那么它就会交由Spring MVC后续流程去处理。
如果控制器返回结果被MappingJackson2HttpMessageConverter进行了转换,那么后续的模型和视图(ModelAndView)就返回null,这样视图解析器和视图渲染将不再被执行,其流程如下:

@ResponseBody注解转换为JSON流程图springboot学习13——springmvc拦截器解析_第2张图片

你可能感兴趣的:(spring,spring,拦截器)