用户认证授权、日志记录 MDC
、编码解码、 UA
检查、多端对应等都需要通过 拦截请求 来进行处理。这时就需要 Servlet
、 Filter
、 Listener
、 Interceptor
这几种组件。而把非 Spring Boot
项目转换成 Spring Boot
项目,需要沿用以前的这些代码,所以有必要了解这它们的 用法 和 生命周期 。
Listener
可以监听 web
服务器中某一个 事件操作 ,并触发注册的 回调函数 。通俗的语言就是在 application
, session
, request
三个对象 创建/消亡 或者 增删改 属性时,自动执行代码的功能组件。
Servlet
是一种运行 服务器端 的 java
应用程序,具有 独立于平台和协议 的特性,并且可以动态的生成 web
页面,它工作在 客户端请求 与 服务器响应 的中间层。
Filter
对 用户请求 进行 预处理 ,接着将请求交给 Servlet
进行 处理 并 生成响应 ,最后 Filter
再对 服务器响应 进行 后处理 。 Filter
是可以复用的代码片段,常用来转换 HTTP
请求 、 响应 和 头信息 。 Filter
不像 Servlet
,它不能产生 响应 ,而是只 修改 对某一资源的 请求 或者 响应 。
类似 面向切面编程 中的 切面 和 通知 ,我们通过 动态代理 对一个 service()
方法添加 通知 进行功能增强。比如说在方法执行前进行 初始化处理 ,在方法执行后进行 后置处理 。 拦截器 的思想和 AOP
类似,区别就是 拦截器 只能对 Controller
的 HTTP
请求进行拦截。
Filter
是基于 函数回调 的,而 Interceptor
则是基于 Java
反射 和 动态代理 。
Filter
依赖于 Servlet
容器,而 Interceptor
不依赖于 Servlet
容器。
Filter
对几乎 所有的请求 起作用,而 Interceptor
只对 Controller
对请求起作用。
对于自定义 Servlet
对请求分发流程:
Filter Servlet Filter
对于自定义 Controller
的请求分发流程:
Filter Interceptor HandlerAdapter Interceptor Interceptor Filter
利用 Spring Initializer
创建一个 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') }
配置一个 Spring Boot
启动入口类,这里需要配置两个注解。
@ServletComponentScan: 允许 Spring Boot
扫描和装载当前 包路径 和 子路径 下配置的 Servlet
。
@EnableWvc: 允许 Spring Boot
配置 Spring MVC
相关自定义的属性,比如:拦截器、资源处理器、消息转换器等。
@EnableWebMvc @ServletComponentScan @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
配置一个 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
,可以全局直接访问。
配置 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>
一个 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()
最核心的三个参数:
Servlet
的 HTTP
请求; Servlet
处理并生成的 HTTP
响应; FilterChain.doFilter(request, response);
解释:一个 过滤器链 对象可以按顺序注册多个 过滤器 。符合当前过滤器过滤条件,即请求 过滤成功 直接放行,则交由下一个 过滤器 进行处理。所有请求过滤完成以后,由 IndexHttpServlet
处理并生成 响应 ,然后在 过滤器链 以相反的方向对 响应 进行后置过滤处理。
配置 IndexController
,用于测试 /index/IndexController
路径是否会被 Filter
过滤和 Interceptor
拦截,并验证两者的先后顺序。
@RestController @RequestMapping("index") public class IndexController { @GetMapping("IndexController") public String index() throws Exception { return "IndexController"; } }
拦截器 Interceptor
只对 Handler
生效。 Spring MVC
会为 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"); } }
在 Spring Boot
中 配置拦截器 很简单,只需要实现 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"); } }
对应的 Spring XML
配置方式如下:
<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
之后执行。
下面简单的看一下 Spring MVC
中心调度器 DispatcherServlet
的 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()
。
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; }
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()
只在 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); } } } }
启动 Spring Boot
应用程序,观察启动时的程序日志,下面我按照 顺序 来分析启动过程中完成了哪些事情。
Spring MVC
的 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
是 Spring MVC
自带的解决乱码的 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 firstIndexFilter
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
关闭 Spring Boot
应用程序时,观察输出日志如下:
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()
方法都被调用了。
访问 http://localhost:8080/index/IndexHttpServlet ,响应页面内容如下:
访问 http://localhost:8080/index/IndexHttpServlet?filter1=filter1 ,响应页面内容如下:
访问 http://localhost:8080/index/IndexHttpServlet?filter1=filter1&filter2=filter2 ,响应页面内容如下:
观察控制台输出日志,验证 过滤器 的过滤顺序正确。
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
生效, 而 自定义 的拦截器生效。
访问 http://localhost:8080/index/IndexController ,响应页面内容如下:
访问 http://localhost:8080/index/IndexController?filter1=filter1 ,响应页面内容如下:
访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2 ,响应页面内容如下:
访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2&interceptor1=interceptor1 ,响应页面内容如下:
访问 http://localhost:8080/index/IndexController?filter1=filter1&filter2=filter2&interceptor1=interceptor1&interceptor2=interceptor2 ,响应页面内容如下:
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
生效。而 过滤器 的优先级高于 拦截器 。