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

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

过滤器与拦截器在SpringMVC请求响应流程情况

  • 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());
    }
}

四、SpringMVC运行原理源码解读

有时间再补上


参考资料:
[1]: Java Servlet 3.1 Specification《Java Servlet 3.1 规范》中文翻译及示例
[2]: 什么是Servlet容器?

发布了35 篇原创文章 · 获赞 32 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/u012995888/article/details/103398278