一张图讲清楚SpringMVC运行原理,以及拦截器&过滤器区别与执行顺序

 2022-08-04
原文作者:猿人日记

一、一张图讲清过滤器拦截器在SpringMVC请求响应流程情况

202208042323537971.png

  • Web服务器 :是用于处理客户端与服务器之间的请求与响应的网络交互过程,Web服务器能处理的请求与响应必须遵循HTTP协议。直白点说,客户端向Web服务器传递的请求数据,和Web服务器向客户端响应的数据都是字符串,或者说Web服务器只能处理静态资源。当客户端发起了HTTP请求,Web服务器首先检查客户端请求的内容是否是静态资源。如果是静态资源,Web服务器会直接找到静态资源返回给客户端。当发现客户端请求的内容是动态内容,Web服务器会把请求转发给Servlet容器来处理。Servlet容器处理完后会把响应转发给Web服务器来转化成符合HTTP协议格式的HTTP响应。
  • Servlet容器 :是 Web 服务器为支持 servlet 功能扩展的部分,实现客户端与Web服务器之间的动态交互。直白点说,就是Web服务器理解不了的HTTP请求,交给了可以理解的Servlet容器。然后,Servlet容器把请求解释成Web服务器可以理解的结果,再转发给Web服务器响应客户端的请求。
  • Filter过滤器 :过滤器Filter和Servlet一样都是基于 Java 的 Web 组件,由Servlet容器进行管理,来生成动态内容。也就是说,过滤器Filter的初始化init()、过滤处理doFilter()、销毁destroy()等方法的执行都是由Servlet容器来调用的。注意,当Web服务器发现HTTP请求的是动态内容,把请求转发给了Servlet容器。Servlet容器原本首先是去查找对应的Servlet来处理,但是当Filter过滤器存在并且匹配请求时,Servlet容器会先拿Filter过滤器对请求先进行处理一遍,再交给Servlet来处理。由于Filter过滤器是使用回调方式,当Servlet处理完后Filter过滤器会继续对Servlet响应进行再处理。由图可见,Servlet容器的生命周期是比Spring容器更早&更久,所以Spring容器中的Bean是无法注入到过滤器Filter中使用的。这个特性也直接导致Filter一般主要用来对请求和响应进行处理,而不对具体的业务进行处理。
  • Interceptor拦截器 :拦截器Interceptor是由Spring管理,存于Spring IOC容器一种组件,也是Spring AOP面向切面编程的一种实现。拦截器是基于动态代理、使用反射机制来实现的。它和Spring IOC中其它组件一样生命周期是由Spring控制,拦截器中是可以注入Spring IOC容器其它Bean进行业务处理的。当然,拦截器也可以实现Filter过滤器全部的作用的。所以,拦截器功能更强大,也建议使用拦截器。
  • SpringMVC :是基于Spring的web组件,实现动态请求处理。其核心类DispatcherServlet,通常称之为前端控制器。所有来自客户端的请求,都会由DispatcherServlet进行请求转发,再由它把响应返回给Servlet容器。具体的运行过程见上图,下文也会由源码解读。

过滤器与拦截器的区别在解读部分已经很清楚

二、Filter过滤器在SpringBoot项目中使用说明

原来在Spring项目中配置Filter过滤器都是在web.xml中进行配置的。在SpringBoot项目中没有了web.xml文件,不过有新的配置方式。

1、注解方式

注解方式有两种方案,如下:

  • @WebFilter+@Component 或 @Configuration+(@Order可选)
    @Slf4j
    @Order(2)
    @Component
    @WebFilter(urlPatterns="/*", filterName="filter2")
    public class Filter2 implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("{}初始化完成",this.getClass().getName());
        }
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            log.info("{}执行过滤开始....",this.getClass().getName());
            filterChain.doFilter(servletRequest,servletResponse);
            log.info("{}执行过滤结束....",this.getClass().getName());
        }
        public void destroy() {
            log.info("{}销毁",this.getClass().getName());
        }
    }
  • @WebFilter+@ServletComponentScan("Filter所在包目录")+(@Order可选)
    @Slf4j
    @Order(2)
    @WebFilter(urlPatterns="/*", filterName="filter2")
    public class Filter2 implements Filter {
        public void init(FilterConfig filterConfig) throws ServletException {
            log.info("{}初始化完成",this.getClass().getName());
        }
        public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
            log.info("{}执行过滤开始....",this.getClass().getName());
            filterChain.doFilter(servletRequest,servletResponse);
            log.info("{}执行过滤结束....",this.getClass().getName());
        }
        public void destroy() {
            log.info("{}销毁",this.getClass().getName());
        }
    }
    @ServletComponentScan("com.mapc.j2ee.filter")
    @SpringBootApplication
    public class J2eeFilterInterceptorServletApplication {
        public static void main(String[] args) {
            SpringApplication.run(J2eeFilterInterceptorServletApplication.class, args);
        }
    }

在SpringBoot入口类添加@ServletComponentScan注解,或者自定义一个FilterConfig类添加@ServletComponentScan+@Configuration组合注解都可以。

2、编码方式

使用FilterRegistrationBean注入。

    @Configuration
    public class FilterConfig {
        @Bean
        public FilterRegistrationBean registerFilter1(){
            FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
            filterRegistrationBean.setFilter(new Filter1());
            filterRegistrationBean.setOrder(1);
            filterRegistrationBean.setUrlPatterns(Collections.singleton("/*"));
            return filterRegistrationBean;
        }
        @Bean
        public FilterRegistrationBean registerFilter2(){
            FilterRegistrationBean filterRegistrationBean=new FilterRegistrationBean();
            filterRegistrationBean.setFilter(new Filter2());
            filterRegistrationBean.setOrder(2);
            filterRegistrationBean.setUrlPatterns(Collections.singleton("/*"));
            return filterRegistrationBean;
        }
    }

三、Interceptor拦截器在SpringBoot项目中使用说明

1、继承WebMvcConfigurationSupport+@Configuration 或 @Component注解

    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport {
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new Interceptor1()).addPathPatterns("/**");
            super.addInterceptors(registry);
        }
    }
    @Slf4j
    @Order(2)
    public class Interceptor1 implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            log.info("{}请求处理前拦截",this.getClass().getName());
            return true;
        }
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
            log.info("{}请求处理后返回ModelAndView拦截",this.getClass().getName());
        }
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            log.info("{}请求处理后返回给前端视图拦截",this.getClass().getName());
        }
    }