ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

2021-02-05 23:08:04  阅读:124  来源: 互联网

标签:filter 拦截器 06 Spring Boot request Filter 2018 response


前言

用户认证授权、日志记录 MDC、编码解码、 UA 检查、多端对应等都需要通过 拦截请求 来进行处理。这时就需要 Servlet、 Filter、 Listener、 Interceptor 这几种组件。而把非 SpringBoot 项目转换成 SpringBoot 项目,需要沿用以前的这些代码,所以有必要了解这它们的 用法 和 生命周期。

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

本系列文章

  1. 实战Spring Boot 2.0系列(一) - 使用Gradle构建Docker镜像

  2. 实战Spring Boot 2.0系列(二) - 全局异常处理和测试

  3. 实战Spring Boot 2.0系列(三) - 使用@Async进行异步调用详解

  4. 实战Spring Boot 2.0系列(四) - 使用WebAsyncTask处理异步任务

  5. 实战Spring Boot 2.0系列(五) - Listener, Servlet和Filter, Controller和Interceptor

正文

1. 几种组件介绍

1.1. 监听器Listener

Listener 可以监听 web 服务器中某一个 事件操作,并触发注册的 回调函数。通俗的语言就是在 application, session, request 三个对象 创建/消亡 或者 增删改 属性时,自动执行代码的功能组件。

1.2. Servlet

Servlet 是一种运行 服务器端 的 java 应用程序,具有 独立于平台和协议 的特性,并且可以动态的生成 web 页面,它工作在 客户端请求 与 服务器响应 的中间层。

1.3. 过滤器Filter

Filter 对 用户请求 进行 预处理,接着将请求交给 Servlet 进行 处理 并 生成响应,最后 Filter 再对 服务器响应 进行 后处理。 Filter 是可以复用的代码片段,常用来转换 HTTP 请求、响应 和 头信息。 Filter 不像 Servlet,它不能产生 响应,而是只 修改 对某一资源的 请求 或者 响应。

1.4. 拦截器Interceptor

类似 面向切面编程 中的 切面 和 通知,我们通过 动态代理 对一个 service()方法添加 通知 进行功能增强。比如说在方法执行前进行 初始化处理,在方法执行后进行 后置处理。拦截器 的思想和 AOP 类似,区别就是 拦截器 只能对 Controller 的 HTTP 请求进行拦截。

2. 过滤器 VS 拦截器

2.1. 两者的区别

  1. Filter 是基于 函数回调的,而 Interceptor 则是基于 Java 反射 和 动态代理。

  2. Filter 依赖于 Servlet 容器,而 Interceptor 不依赖于 Servlet 容器。

  3. Filter 对几乎 所有的请求 起作用,而 Interceptor 只对 Controller 对请求起作用。

2.2. 执行顺序

对于自定义 Servlet 对请求分发流程:

  1. Filter 过滤请求处理;

  2. Servlet 处理请求;

  3. Filter 过滤响应处理。

对于自定义 Controller 的请求分发流程:

  1. Filter 过滤请求处理;

  2. Interceptor 拦截请求处理;

  3. 对应的 HandlerAdapter 处理请求;

  4. Interceptor 拦截响应处理;

  5. Interceptor 的最终处理;

  6. Filter 过滤响应处理。

3. 环境准备

配置gradle依赖

利用 SpringInitializer 创建一个 gradle 项目 spring-boot-listener-servlet-filter-interceptor,创建时添加相关依赖。得到的初始 build.gradle 如下:

buildscript {

    ext {

        springBootVersion = '2.0.3.RELEASE'

    }

    repositories {

        mavenCentral()

    }

    dependencies {

        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")

    }

}

apply plugin: 'java'

apply plugin: 'eclipse'

apply plugin: 'org.springframework.boot'

apply plugin: 'io.spring.dependency-management'

group = 'io.ostenant.springboot.sample'

version = '0.0.1-SNAPSHOT'

sourceCompatibility = 1.8

repositories {

    mavenCentral()

}

dependencies {

    compile('org.springframework.boot:spring-boot-starter-web')

    testCompile('org.springframework.boot:spring-boot-starter-test')

}

配置启动入口类

配置一个 SpringBoot 启动入口类,这里需要配置两个注解。

  • @ServletComponentScan: 允许 SpringBoot 扫描和装载当前 包路径 和 子路径 下配置的 Servlet。

  • @EnableWvc: 允许 SpringBoot 配置 SpringMVC 相关自定义的属性,比如:拦截器、资源处理器、消息转换器等。
    @EnableWebMvc

    @ServletComponentScan

    @SpringBootApplication

    public class Application {

        public static void main(String[] args) {

            SpringApplication.run(Application.class, args);

        }

    }

4. 配置监听器Listener

配置一个 ServletContext 监听器,使用 @WebListener 标示即可。在 Servlet 容器 初始化 过程中, contextInitialized() 方法会被调用,在容器 销毁 时会调用 contextDestroyed()。

@WebListener

public class IndexServletContextListener implements ServletContextListener {

    private static final Logger LOGGER = LoggerFactory.getLogger(IndexServletContextListener.class);

    public static final String INITIAL_CONTENT = "Content created in servlet Context";

    @Override

    public void contextInitialized(ServletContextEvent sce) {

        LOGGER.info("Start to initialize servlet context");

        ServletContext servletContext = sce.getServletContext();

        servletContext.setAttribute("content", INITIAL_CONTENT);

    }

    @Override

    public void contextDestroyed(ServletContextEvent sce) {

        LOGGER.info("Destroy servlet context");

    }

}

这里在容器初始化时,往 ServletContext 上下文设置了参数名称为 INITIAL_CONTENT,可以全局直接访问。

5. 配置Servlet

配置 IndexHttpServlet,重写 HttpServlet 的 doGet() 方法,直接输出 IndexHttpServlet 定义的 初始化参数 和在 IndexServletContextListener设置的 ServletContext 上下文参数。

@WebServlet(name = "IndexHttpServlet",

        displayName = "indexHttpServlet",

        urlPatterns = {"/index/IndexHttpServlet"},

        initParams = {

                @WebInitParam(name = "createdBy", value = "Icarus"),

                @WebInitParam(name = "createdOn", value = "2018-06-20")

        }

)

public class IndexHttpServlet extends HttpServlet {

    @Override

    protected void doGet(HttpServletRequest req, HttpServletResponse resp)

            throws IOException {

        resp.getWriter().println(format("Created by %s", getInitParameter("createdBy")));

        resp.getWriter().println(format("Created on %s", getInitParameter("createdOn")));

        resp.getWriter().println(format("Servlet context param: %s",

                req.getServletContext().getAttribute("content")));

    }

}

配置 @WebServlet 注解用于注册这个 Servlet, @WebServlet 注解的 各个参数 分别对应 web.xml 中的配置:

<servlet-mapping>  

    <servlet-name>IndexHttpServlet</servlet-name>

    <url-pattern>/index/IndexHttpServlet</url-pattern>

</servlet-mapping>

<servlet>  

    <servlet-name>IndexHttpServlet</servlet-name>  

    <servlet-class>io.ostenant.springboot.sample.servlet.IndexHttpServlet</servlet-class>

    <init-param>

        <param-name>createdBy</param-name>

        <param-value>Icarus</param-value>

    </init-param>

    <init-param>

        <param-name>createdOn</param-name>

        <param-value>2018-06-20</param-value>

    </init-param>

</servlet>  

6. 配置过滤器Filter

一个 Servlet 请求可以经由多个 Filter 进行过滤,最终由 Servlet 处理并响应客户端。这里配置两个过滤器示例:

FirstIndexFilter.java

@WebFilter(filterName = "firstIndexFilter",

        displayName = "firstIndexFilter",

        urlPatterns = {"/index/*"},

        initParams = @WebInitParam(

                name = "firstIndexFilterInitParam",

                value = "io.ostenant.springboot.sample.filter.FirstIndexFilter")

)

public class FirstIndexFilter implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(FirstIndexFilter.class);

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

        LOGGER.info("Register a new filter {}", filterConfig.getFilterName());

    }

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

            throws IOException, ServletException {

        LOGGER.info("FirstIndexFilter pre filter the request");

        String filter = request.getParameter("filter1");

        if (isEmpty(filter)) {

            response.getWriter().println("Filtered by firstIndexFilter, " +

                    "please set request parameter \"filter1\"");

            return;

        }

        chain.doFilter(request, response);

        LOGGER.info("FirstIndexFilter post filter the response");

    }

    @Override

    public void destroy() {

        LOGGER.info("Destroy filter {}", getClass().getName());

    }

}

以上 @WebFilter 相关的配置属性,对应于 web.xml 的配置如下:

<filter-mapping>

    <filter-name>firstIndexFilter</filter-name>

    <filter-class>io.ostenant.springboot.sample.filter.FirstIndexFilter</filter-class>

    <url-pattern>/index/*</url-pattern>

    <init-param>

        <param-name>firstIndexFilterInitParam</param-name>

        <param-value>io.ostenant.springboot.sample.filter.FirstIndexFilter</param-value>

    </init-param>

</filter-mapping>

配置 FirstIndexFilter,使用 @WebFilter 注解进行标示。当 FirstIndexFilter 初始化时,会执行 init() 方法。每次请求路径匹配 urlPatterns 配置的路径时,就会进入 doFilter() 方法进行具体的 请求 和 响应过滤。

当 HTTP 请求携带 filter1 参数时,请求会被放行;否则,直接 过滤中断,结束请求处理。

SecondIndexFilter.java

@WebFilter(filterName = "secondIndexFilter",

        displayName = "secondIndexFilter",

        urlPatterns = {"/index/*"},

        initParams = @WebInitParam(

                name = "secondIndexFilterInitParam",

                value = "io.ostenant.springboot.sample.filter.SecondIndexFilter")

)

public class SecondIndexFilter implements Filter {

    private static final Logger LOGGER = LoggerFactory.getLogger(SecondIndexFilter.class);

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

        LOGGER.info("Register a new filter {}", filterConfig.getFilterName());

    }

    @Override

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

            throws IOException, ServletException {

        LOGGER.info("SecondIndexFilter pre filter the request");

        String filter = request.getParameter("filter2");

        if (isEmpty(filter)) {

            response.getWriter().println("Filtered by firstIndexFilter, " +

                    "please set request parameter \"filter2\"");

            return;

        }

        chain.doFilter(request, response);

        LOGGER.info("SecondIndexFilter post filter the response");

    }

    @Override

    public void destroy() {

        LOGGER.info("Destroy filter {}", getClass().getName());

    }

}

以上 @WebFilter 相关的配置属性,对应于 web.xml 的配置如下:

<filter-mapping>

    <filter-name>secondIndexFilter</filter-name>

    <filter-class>io.ostenant.springboot.sample.filter.SecondIndexFilter</filter-class>

    <url-pattern>/index/*</url-pattern>

    <init-param>

        <param-name>secondIndexFilterInitParam</param-name>

        <param-value>io.ostenant.springboot.sample.filter.SecondIndexFilter</param-value>

    </init-param>

</filter-mapping>

配置 SecondIndexFilter,使用 @WebFilter 注解进行标示。当 SecondIndexFilter 初始化时,会执行 init() 方法。每次请求路径匹配 urlPatterns 配置的路径时,就会进入 doFilter() 方法进行具体的 请求 和 响应过滤。

当 HTTP 请求携带 filter2 参数时,请求会被放行;否则,直接 过滤中断,结束请求处理。

来看看 doFilter() 最核心的三个参数:

  • ServletRequest: 未到达 Servlet 的 HTTP 请求;

  • ServletResponse: 由 Servlet 处理并生成的 HTTP 响应;

  • FilterChain: 过滤器链 对象,可以按顺序注册多个 过滤器。
    FilterChain.doFilter(request, response);

解释: 一个 过滤器链 对象可以按顺序注册多个 过滤器。符合当前过滤器过滤条件,即请求 过滤成功 直接放行,则交由下一个 过滤器 进行处理。所有请求过滤完成以后,由 IndexHttpServlet 处理并生成 响应,然后在 过滤器链 以相反的方向对 响应 进行后置过滤处理。

配置控制器Controller

配置 IndexController,用于测试 /index/IndexController 路径是否会被 Filter 过滤和 Interceptor 拦截,并验证两者的先后顺序。

@RestController

@RequestMapping("index")

public class IndexController {

    @GetMapping("IndexController")

    public String index() throws Exception {

        return "IndexController";

    }

}

7. 配置拦截器Interceptor

拦截器 Interceptor 只对 Handler 生效。 SpringMVC 会为 Controller 中的每个 请求方法 实例化为一个 Handler对象,由 HandlerMapping 对象路由请求到具体的 Handler,然后由 HandlerAdapter 通过反射进行请求 处理 和 响应,这中间就穿插着 拦截处理。

编写拦截器

为了区分日志,下面同样对 IndexController 配置两个拦截器类:

FirstIndexInterceptor.java

public class FirstIndexInterceptor implements HandlerInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(FirstIndexInterceptor.class);

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        LOGGER.info("FirstIndexInterceptor pre intercepted the request");

        String interceptor = request.getParameter("interceptor1");

        if (isEmpty(interceptor)) {

            response.getWriter().println("Filtered by FirstIndexFilter, " +

                    "please set request parameter \"interceptor1\"");

            return false;

        }

        return true;

    }

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        LOGGER.info("FirstIndexInterceptor post intercepted the response");

    }

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        LOGGER.info("FirstIndexInterceptor do something after request completed");

    }

}

SecondIndexInterceptor.java

public class SecondIndexInterceptor implements HandlerInterceptor {

    private static final Logger LOGGER = LoggerFactory.getLogger(SecondIndexInterceptor.class);

    @Override

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        LOGGER.info("SecondIndexInterceptor pre intercepted the request");

        String interceptor = request.getParameter("interceptor2");

        if (isEmpty(interceptor)) {

            response.getWriter().println("Filtered by SecondIndexInterceptor, " +

                    "please set request parameter \"interceptor2\"");

            return false;

        }

        return true;

    }

    @Override

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        LOGGER.info("SecondIndexInterceptor post intercepted the response");

    }

    @Override

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

        LOGGER.info("SecondIndexInterceptor do something after request completed");

    }

}

配置拦截器

在 SpringBoot 中 配置拦截器 很简单,只需要实现 WebMvcConfigurer 接口,在 addInterceptors() 方法中通过 InterceptorRegistry 添加 拦截器和 匹配路径 即可。

@Configuration

public class WebConfiguration implements WebMvcConfigurer {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebConfiguration.class);

    @Override

    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(new FirstIndexInterceptor()).addPathPatterns("/index/**");

        registry.addInterceptor(new SecondIndexInterceptor()).addPathPatterns("/index/**");

        LOGGER.info("Register FirstIndexInterceptor and SecondIndexInterceptor onto InterceptorRegistry");

    }

}

对应的 SpringXML 配置方式如下:

<bean id="firstIndexInterceptor"

class="io.ostenant.springboot.sample.interceptor.FirstIndexInterceptor"></bean>

<bean id="secondIndexInterceptor"

class="io.ostenant.springboot.sample.interceptor.SecondIndexInterceptor"></bean>

<mvc:interceptors>

    <mvc:interceptor>

        <mvc:mapping path="/index/**" />

        <ref local="firstIndexInterceptor" />

    </mvc:interceptor>

    <mvc:interceptor>

        <mvc:mapping path="/index/**" />

        <ref local="secondIndexInterceptor" />

    </mvc:interceptor>

</mvc:interceptors>

原理剖析

我们通过实现 HandlerInterceptor 接口来开发一个 拦截器,来看看 HandlerInterceptor 接口的三个重要的方法:

  • preHandle(): 在 controller 接收请求、处理 request 之前执行,返回值为 boolean,返回值为 true 时接着执行 postHandle() 和 afterCompletion() 方法;如果返回 false 则 中断 执行。

  • postHandle(): 在 controller 处理请求之后, ModelAndView 处理前执行,可以对 响应结果 进行修改。

  • afterCompletion(): 在 DispatchServlet 对本次请求处理完成,即生成 ModelAndView 之后执行。

下面简单的看一下 SpringMVC 中心调度器 DispatcherServlet 的 doDispatch() 方法的原理,重点关注 拦截器 的以上三个方法的执行顺序。

  • doDispatch(): DispatchServlet 处理请求分发的核心方法。
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

        HttpServletRequest processedRequest = request;

        HandlerExecutionChain mappedHandler = null;

        boolean multipartRequestParsed = false;

        WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);

        try {

            ModelAndView mv = null;

            Exception dispatchException = null;

            try {

                processedRequest = checkMultipart(request);

                multipartRequestParsed = (processedRequest != request);

                // Determine handler for the current request.

                mappedHandler = getHandler(processedRequest);

                if (mappedHandler == null) {

                    noHandlerFound(processedRequest, response);

                    return;

                }

                // Determine handler adapter for the current request.

                HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

                // Process last-modified header, if supported by the handler.

                String method = request.getMethod();

                boolean isGet = "GET".equals(method);

                if (isGet || "HEAD".equals(method)) {

                    long lastModified = ha.getLastModified(request, mappedHandler.getHandler());

                    if (logger.isDebugEnabled()) {

                        logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);

                    }

                    if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {

                        return;

                    }

                }

                // 1. 按从前往后的顺序调用各个拦截器preHandle()方法

                if (!mappedHandler.applyPreHandle(processedRequest, response)) {

                    return;

                }

                // 2. HandlerAdapter开始真正的请求处理并生产响应视图对象

                mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

                if (asyncManager.isConcurrentHandlingStarted()) {

                    return;

                }

                applyDefaultViewName(processedRequest, mv);

                // 3. 按照从后往前的顺序依次调用各个拦截器的postHandle()方法

                mappedHandler.applyPostHandle(processedRequest, response, mv);

            } catch (Exception ex) {

                dispatchException = ex;

            } catch (Throwable err) {

                dispatchException = new NestedServletException("Handler dispatch failed", err);

            }

            processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

        } catch (Exception ex) {

            // 4. 最终会调用拦截器的afterCompletion()方法

            triggerAfterCompletion(processedRequest, response, mappedHandler, ex);

        } catch (Throwable err) {

            // 4. 最终会调用拦截器的afterCompletion()方法

            triggerAfterCompletion(processedRequest, response, mappedHandler,

                    new NestedServletException("Handler processing failed", err));

        } finally {

            if (asyncManager.isConcurrentHandlingStarted()) {

                if (mappedHandler != null) {

                    mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);

                }

            } else {

                if (multipartRequestParsed) {

                    cleanupMultipart(processedRequest);

                }

            }

        }

    }

上面注释的几个 HandlerExecutionChain 的方法: applyPreHandle()、 applyPostHandle() 和 triggerAfterCompletion()。

  • applyPreHandle(): 按 从前往后 的顺序调用各个拦截器的 preHandle() 方法。任意一个 HandlerInterceptor 拦截返回 false ,则 preHandle() 返回 false,记录拦截器的位置 interceptorIndex,然后中断拦截处理,最终触发 AfterCompletion() 方法并返回 false。
    boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();

        if (!ObjectUtils.isEmpty(interceptors)) {

            for (int i = 0; i < interceptors.length; i++) {

                HandlerInterceptor interceptor = interceptors[i];

                if (!interceptor.preHandle(request, response, this.handler)) {

                    triggerAfterCompletion(request, response, null);

                    return false;

                }

                this.interceptorIndex = i;

            }

        }

        return true;

    }
  • applyPostHandle(): 按照 从后往前 的顺序依次调用各个拦截器的 postHandle() 方法。只有当所有 HandlerInterceptor 的 preHandle() 方法返回 true 时,才有机会执行到 applyPostHandle() 方法。
    void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)

            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();

        if (!ObjectUtils.isEmpty(interceptors)) {

            for (int i = interceptors.length - 1; i >= 0; i--) {

                HandlerInterceptor interceptor = interceptors[i];

                interceptor.postHandle(request, response, this.handler, mv);

            }

        }

    }
  • triggerAfterCompletion(): triggerAfterCompletion() 只在 preHandle() 方法返回 false 和 程序抛出异常 时执行。在 preHandle() 方法中,通过 interceptorIndex 记录了返回 false 的 拦截器索引。一旦 applyPreHandle() 方法返回 false,则从当前返回 false 的拦截器 从后往前 的执行 afterCompletion() 方法。
    void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)

            throws Exception {

        HandlerInterceptor[] interceptors = getInterceptors();

        if (!ObjectUtils.isEmpty(interceptors)) {

            for (int i = this.interceptorIndex; i >= 0; i--) {

                HandlerInterceptor interceptor = interceptors[i];

                try {

                    interceptor.afterCompletion(request, response, this.handler, ex);

                }

                catch (Throwable ex2) {

                    logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);

                }

            }

        }

    }

8. 开始测试

生命周期测试

启动 SpringBoot 应用程序,观察启动时的程序日志,下面我按照 顺序 来分析启动过程中完成了哪些事情。

  • 注册 SpringMVC 的 dispatcherServlet 和自定义的 IndexHttpServlet。
    2018-06-23 09:39:55.400  INFO 12301 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet dispatcherServlet mapped to [/]

    2018-06-23 09:39:55.404  INFO 12301 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean  : Servlet IndexHttpServlet mapped to [/index/IndexHttpServlet]

注意: dispatcherServlet 的 load-up-onstartup 为 1,会优先于其他 Servlet 进行加载。

  • 按照先后顺序,将所有的过滤器 Filter 对象与路径进行映射,其中 characterEncodingFilter 是 SpringMVC 自带的解决乱码的 Filter。
    2018-06-23 09:39:55.408  INFO 12301 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'characterEncodingFilter' to: [/*]

    2018-06-23 09:39:55.409  INFO 12301 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'firstIndexFilter' to urls: [/index/*]

    2018-06-23 09:39:55.409  INFO 12301 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean   : Mapping filter: 'secondIndexFilter' to urls: [/index/*]
  • 初始化 IndexServletContextListener,并执行 contextInitialized()方法进行上下文初始化操作。
    2018-06-23 09:39:55.429  INFO 12301 --- [ost-startStop-1] i.o.s.s.l.IndexServletContextListener    : Start to initialize servlet context
  • 依次执行 Filter 的 init() 方法进行初始化处理。
    2018-06-23 09:39:55.432  INFO 12301 --- [ost-startStop-1] i.o.s.sample.filter.SecondIndexFilter     : Register a new filter secondIndexFilter

    2018-06-23 09:39:55.434  INFO 12301 --- [ost-startStop-1] i.o.s.sample.filter.FirstIndexFilter      : Register a new filter f
irstIndexFilter
  • 创建、初始化拦截器,并统一注册到 InterceptorRegistry 上。
    2018-06-23 09:39:55.502  INFO 13150 --- [           main] i.o.s.s.interceptor.WebConfiguration     : Register FirstIndexInterceptor and SecondIndexInterceptor onto InterceptorRegistry
  • 对 IndexController 进行处理,把 请求 URI 和 处理方法 映射到 HandlerMapping 上并进行缓存。
    2018-06-23 09:39:55.541  INFO 12301 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/index/IndexController],methods=[GET]}" onto public java.lang.String io.ostenant.springboot.sample.controller.IndexController.index() throws java.lang.Exception

关闭 SpringBoot 应用程序时,观察输出日志如下:

2018-06-23 10:07:03.294  INFO 12301 --- [ost-startStop-2] i.o.s.sample.filter.FirstIndexFilter     : Destroy filter io.ostenant.springboot.sample.filter.SecondIndexFilter

2018-06-23 10:07:03.294  INFO 12301 --- [ost-startStop-2] i.o.s.sample.filter.FirstIndexFilter     : Destroy filter io.ostenant.springboot.sample.filter.FirstIndexFilter

2018-06-23 10:07:03.294  INFO 12301 --- [ost-startStop-2] i.o.s.s.l.IndexServletContextListener    : Destroy servlet context

可以看到上面配置的过滤器的 destroy() 方法和 IndexServletContextListener 的 contextDestroyed() 方法都被调用了。

访问控制测试

Servlet测试

访问 http://localhost:8080/index/IndexHttpServlet,响应页面内容如下
实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter
访问 http://localhost:8080/index/IndexHttpServlet?filter1=filter1,响应页面内容如下
实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter
访问 http://localhost:8080/index/IndexHttpServlet?filter1=filter1&filter2=filter2,响应页面内容如下
实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter
观察控制台输出日志,验证 过滤器 的过滤顺序正确。

2018-06-23 10:19:47.944  INFO 13150 --- [nio-8080-exec-1] i.o.s.sample.filter.FirstIndexFilter     : FirstIndexFilter pre filter the request

2018-06-23 10:19:47.944  INFO 13150 --- [nio-8080-exec-1] i.o.s.sample.filter.SecondIndexFilter    : SecondIndexFilter pre filter the request

2018-06-23 10:19:47.944  INFO 13150 --- [nio-8080-exec-1] i.o.s.sample.filter.SecondIndexFilter    : SecondIndexFilter post filter the response

2018-06-23 10:19:47.944  INFO 13150 --- [nio-8080-exec-1] i.o.s.sample.filter.FirstIndexFilter     : FirstIndexFilter post filter the response
结论: 自定义的 过滤器 对 IndexHttpServlet 生效, 而 自定义 的拦截器生效。

controller测试

访问 http://localhost:8080/index/IndexController,响应页面内容如下

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

访问 http://localhost:8080/index/IndexController?filter1=filter1,响应页面内容如下

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2,响应页面内容如下

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2&interceptor1=interceptor1,响应页面内容如下

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2&interceptor1=interceptor1&interceptor2=interceptor2,响应页面内容如下

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

2018-06-23 10:21:42.533  INFO 13150 --- [nio-8080-exec-4] i.o.s.sample.filter.FirstIndexFilter     : FirstIndexFilter pre filter the request

2018-06-23 10:21:42.533  INFO 13150 --- [nio-8080-exec-4] i.o.s.sample.filter.SecondIndexFilter    : SecondIndexFilter pre filter the request

2018-06-23 10:21:42.534  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.FirstIndexInterceptor          : FirstIndexInterceptor pre intercepted the request

2018-06-23 10:21:42.534  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.SecondIndexInterceptor         : SecondIndexInterceptor pre intercepted the request

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.SecondIndexInterceptor         : SecondIndexInterceptor post intercepted the response

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.FirstIndexInterceptor          : FirstIndexInterceptor post intercepted the response

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.SecondIndexInterceptor         : SecondIndexInterceptor do something after request completed

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.s.i.FirstIndexInterceptor          : FirstIndexInterceptor do something after request completed

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.sample.filter.SecondIndexFilter    : SecondIndexFilter post filter the response

2018-06-23 10:21:42.535  INFO 13150 --- [nio-8080-exec-4] i.o.s.sample.filter.FirstIndexFilter     : FirstIndexFilter post filter the response

结论: 自定义的 过滤器 和 拦截器 对 控制器 Controller 生效。而 过滤器 的优先级高于 拦截器。

小结

本文详细介绍了 Listener, Servlet, Filter, Controller 和 Interceptor 等 Web 多种组件的功能、方法、顺序、作用域和生命周期。给出了详细的示例代码,结合 源码 分析了流程,结合 测试 验证了结论。长篇大论,希望大家对 Servlet 组件和 SpringMVC 的框架组件有了更清晰的认识。

欢迎关注技术公众号: 零壹技术栈

实战Spring Boot 2.0系列(五) - Listener, Servlet, Filter

本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。

标签:filter,拦截器,06,Spring,Boot,request,Filter,2018,response
来源: https://blog.51cto.com/15080016/2620599

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有