前言
用户认证授权、日志记录 MDC
、编码解码、UA
检查、多端对应等都需要通过 拦截请求 来进行处理。这时就需要 Servlet
、Filter
、Listener
、Interceptor
这几种组件。而把非 Spring Boot
项目转换成 Spring Boot
项目,需要沿用以前的这些代码,所以有必要了解这它们的 用法 和 生命周期。
正文
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. 两者的区别
Filter
是基于 函数回调的,而Interceptor
则是基于Java
反射 和 动态代理。Filter
依赖于Servlet
容器,而Interceptor
不依赖于Servlet
容器。Filter
对几乎 所有的请求 起作用,而Interceptor
只对Controller
对请求起作用。
2.2. 执行顺序
对于自定义 Servlet
对请求分发流程:
Filter
过滤请求处理;Servlet
处理请求;Filter
过滤响应处理。
对于自定义 Controller
的请求分发流程:
Filter
过滤请求处理;Interceptor
拦截请求处理;- 对应的
HandlerAdapter
处理请求; Interceptor
拦截响应处理;Interceptor
的最终处理;Filter
过滤响应处理。
3. 环境准备
配置gradle依赖
利用 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);
}
}
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></