## 过滤器 Filter
当客户端访问服务器的某些资源时,过滤器可以将请求拦截,一般用于完成通用的操作,例如登录验证、统一编码处理、敏感字符过滤等。与 Servlet 地位相同,Filter 是 JavaWeb 的三大组件之一。
## 使用 Filter
要使用过滤器 Filter,首先定义一个类实现 Filter 接口,实现其中的抽象方法,然后通过 `web.xml` 文件或者注解配置该类,例如使用注解配置
```java
package cool.yzt.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 配置 Filter 要拦截的资源路径,这里拦截所有资源
@WebFilter(urlPatterns = {"/*"})
public class FilterDemo1 implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 放行请求前的操作
System.out.println("第一次执行过滤器");
// 放行
filterChain.doFilter(servletRequest,servletResponse);
// 访问到资源后响应时的操作
System.out.println("第二次执行过滤器");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
```
注意到,一次资源访问会经过过滤器两次,所以一般在放行请求前,会对 `request` 作增强操作,而访问资源后再次经过过滤器时,一般是对 `response` 做出增强操作。
### 关于 Filter 的配置
如果使用注解配置,其中的 `urlPatterns` 属性配置的是**要拦截的资源**,注意这点和 Servlet 不同,具体来说可以有以下几种情况
* 拦截某个具体资源,例如 `urlPatterns = {"/index.html"}`
* 拦截某个目录。例如 `urlPatterns = {"/user/"}` 即拦截 `user` 目录下的所有资源
* 拦截某个后缀名的资源,例如 `urlPatterns = {"*.jsp"}` 即拦截所有 jsp 文件
* 拦截所有资源,`urlPatterns = {"/*"}`。
除了可以配置 `urlPatterns` 属性,有时还需要配置 `dispatcherTypes` 属性,即拦截使用该方式的访问,而不拦截其他方式,主要有以下取值
* `REQUEST`,默认值,指浏览器直接请求资源
* `FORWARD`,转发方式访问资源
* `INCLUDE`,包含方式访问资源
* `ERROR`,错误跳转资源
* `ASYNC`,异步访问资源
如果使用 `web.xml` 文件配置
```xml
demo1
cn.itcast.web.filter.FilterDemo1
demo1
/*
FORWARD
```
### Filter 链
有时一个过滤器无法满足需求,可能会用到一系列的过滤器组成过滤器链
通过注解配置的 Filter 链,拦截请求时,按照其类名的字典顺序从小到大依次执行,访问资源后对客户端做出响应时,拦截请求时先执行的过滤器反而后执行执行(可以看做是栈,先进后出)
通过 `web.xml` 配置的 Filter 链,先于注解配置的 Filter 执行,且按照配置文件从上到下的顺序依次执行,与类名无关
### Filter 的生命周期
注意到 `Filter` 接口三个必须实现的方法:`init()`、`doFilter()`、`destroy`
1. `init()`:在服务器启动后,会创建 `Filter` 对象,然后调用 `init()` 方法,所以该方法只执行一次,一般用于加载资源
2. `doFilter()`:对于被拦截资源的每一次请求,该方法都会执行,所以会执行多次
3. `destroy()`:在服务器关闭后,`Filter` 对象被销毁,如果服务器是正常关闭,则会执行 `destroy()` 方法,所以该方法只执行一次,一般用于释放资源
## 监听器 Listener
监听器同样是 JavaWeb 的三大组件之一,监听器的本质就是一个对象,事件监听机制最关键的就是注册监听,就是指将事件、事件源、监听器绑定在一起,当事件源上某个事件发生时,监听器的代码就会被执行。
### ServletContextListener
`ServletContextListener` 是一个常用的监听器,用于监听 `ServletContext` 对象的创建和销毁
创建一个 Listener 类,实现 `ServletContextListener` 接口,发现有两个需要实现的接口,并且监听器需要加上 `@WebListener` 注解,服务器启动后,`ServletContext` 对象会自动创建,所以 `ServletContextListener` 的 `contextInitialized()` 方法会被自动调用,一般用于加载整个项目都需要的资源,而当服务器关闭时,`ServletContext` 对象会被销毁,如果服务器正常关闭,`ServletContextListener` 的 `contextDestroyed()` 方法会被自动调用。
```java
// 监听 ServletContext 对象的创建和销毁
//
@WebListener
public class ListenerDemo implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("ServletContext对象被创建了");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("ServletContext对象被销毁了");
}
}
```
### Listener 配置
1. `web.xml` 配置
```xml
cool.yzt.listener.ListenerDemo
```
2. 注解配置
```java
@WebListener
```
## 参考
* [黑马 JavaWeb](https://www.bilibili.com/video/BV1J4411877m)

过滤器和监听器