用户认证授权、日志记录 MDC、编码解码、UA 检查、多端对应等都需要通过 拦截请求 来进行处理。这时就需要 Servlet、Filter、Listener、Interceptor 这几种组件。而把非 Spring Boot 项目转换成 Spring Boot 项目需要沿用以前的这些代码所以有必要了解这它们的 用法 和 生命周期。
正文 1. 几种组件介绍 1.1. 监听器ListenerListener 可以监听 web 服务器中某一个 事件操作并触发注册的 回调函数。通俗的语言就是在 applicationsessionrequest 三个对象 创建/消亡 或者 增删改 属性时自动执行代码的功能组件。
1.2. ServletServlet 是一种运行 服务器端 的 java 应用程序具有 独立于平台和协议 的特性并且可以动态的生成 web 页面它工作在 客户端请求 与 服务器响应 的中间层。
1.3. 过滤器FilterFilter 对 用户请求 进行 预处理接着将请求交给 Servlet 进行 处理 并 生成响应最后 Filter 再对 服务器响应 进行 后处理。Filter 是可以复用的代码片段常用来转换 HTTP 请求、响应 和 头信息。Filter 不像 Servlet它不能产生 响应而是只 修改 对某一资源的 请求 或者 响应。
1.4. 拦截器Interceptor类似 面向切面编程 中的 切面 和 通知我们通过 动态代理 对一个 service() 方法添加 通知 进行功能增强。比如说在方法执行前进行 初始化处理在方法执行后进行 后置处理。拦截器 的思想和 AOP 类似区别就是 拦截器 只能对 Controller 的 HTTP 请求进行拦截。
2. 过滤器 VS 拦截器 2.1. 两者的区别-
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-web-async-task创建时添加相关依赖。得到的初始 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);
}
}
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 = "Vainlgory"),
@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 中的配置
IndexHttpServlet
/index/IndexHttpServlet
IndexHttpServlet
io.ostenant.springboot.sample.servlet.IndexHttpServlet
createdBy
Vainlgory
createdOn
2018-06-20
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 的配置如下
firstIndexFilter
io.ostenant.springboot.sample.filter.FirstIndexFilter
/index/*
firstIndexFilterInitParam
io.ostenant.springboot.sample.filter.FirstIndexFilter
配置 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 的配置如下
secondIndexFilter
io.ostenant.springboot.sample.filter.SecondIndexFilter
/index/*
secondIndexFilterInitParam
io.ostenant.springboot.sample.filter.SecondIndexFilter
配置 SecondIndexFilter使用 @WebFilter 注解进行标示。当 SecondIndexFilter 初始化时会执行 init() 方法。每次请求路径匹配 urlPatterns 配置的路径时就会进入 doFilter() 方法进行具体的 请求 和 响应过滤。
当 HTTP 请求携带 filter2 参数时请求会被放行否则直接 过滤中断结束请求处理。
来看看 doFilter() 最核心的三个参数
- ServletRequest: 未到达 Servlet 的 HTTP 请求
- ServletResponse: 由 Servlet 处理并生成的 HTTP 响应
- FilterChain: 过滤器链 对象可以按顺序注册多个 过滤器。
FilterChain.doFilter(request, response);
配置控制器Controller解释 一个 过滤器链 对象可以按顺序注册多个 过滤器。符合当前过滤器过滤条件即请求 过滤成功 直接放行则交由下一个 过滤器 进行处理。所有请求过滤完成以后由 IndexHttpServlet 处理并生成 响应然后在 过滤器链 以相反的方向对 响应 进行后置过滤处理。
配置 IndexController用于测试 /index/IndexController 路径是否会被 Filter 过滤和 Interceptor 拦截并验证两者的先后顺序。
@RestController
@RequestMapping("index")
public class IndexController {
@GetMapping("IndexController")
public String index() throws Exception {
return "IndexController";
}
}
7. 配置拦截器Interceptor
拦截器 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 配置方式如下
原理剖析
我们通过实现 HandlerInterceptor 接口来开发一个 拦截器来看看 HandlerInterceptor 接口的三个重要的方法
-
preHandle(): 在 controller 接收请求、处理 request 之前执行返回值为 boolean返回值为 true 时接着执行 postHandle() 和 afterCompletion() 方法如果返回 false 则 中断 执行。
-
postHandle(): 在 controller 处理请求之后 ModelAndView 处理前执行可以对 响应结果 进行修改。
-
afterCompletion(): 在 DispatchServlet 对本次请求处理完成即生成 ModelAndView 之后执行。
下面简单的看一下 Spring MVC 中心调度器 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. 开始测试
生命周期测试
启动 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() 方法都被调用了。
访问控制测试 Servlet测试访问 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
controller测试结论 自定义的 过滤器 对 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 生效。而 过滤器 的优先级高于 拦截器。
本文详细介绍了 ListenerServletFilterController 和 Interceptor 等 Web 多种组件的功能、方法、顺序、作用域和生命周期。给出了详细的示例代码结合 源码 分析了流程结合 测试 验证了结论。长篇大论希望大家对 Servlet 组件和 Spring MVC 的框架组件有了更清晰的认识。



