Servlet系列(四):过滤器和监听器

Filter接口

Servlet Filter也称为过滤器,用于拦截请求和响应,从而动态地改变包含在请求和响应中的信息。即在用户请求request到达相应的Servlet之前对其进行预处理,在服务器请求response到达客户端之前对其进行处理。

由于过滤器执行过程类似于「横切」(类似于Spring MVC中的拦截器),过滤器常常用于请求身份验证、请求乱码处理、日志记录等。

实现Filter

为了实现一个过滤器Filter,需要实现javax.servlet.Servlet接口,该接口定义了以下三个方法:

1
2
3
4
5
6
7
8
public interface Filter {

public void init(FilterConfig filterConfig) throws ServletException;

public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;

public void destroy();
}

这三个方法分别对应了Filter声明周期的三个阶段:

  1. 初始化阶段,Servlet容器负责加载和实例化Filter,读取web.xml配置文件中的配置信息对过滤器加载和实例化,之后调用过滤器的init()方法。在整个声明周期中,init方法只执行一次,可以在该方法中执行一些初始化操作。
  2. 过滤阶段,当用户发送请求访问Web资源时,Servlet容器根据过滤器的配置规则(拦截路径)将请求进行拦截,并调用doFilter()方法,该方法有三个参数,分别是被拦截的请求request和响应response,还有过滤链chain对象,前二者用于处理用户请求和响应,chain对象用于控制请求的放行与否。
  3. 销毁阶段,Servlet容器只在最开始创建和加载过滤器,此后过滤器将一直存在内存中,知道最后Servlet容器关闭的时候,会调用过滤器的destroy()方法,该方法在整个生命周期中也只执行一次。

接下来,我们编写两个过滤器演示过滤器的工作过程:

过滤器一:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("HelloFilter初始化....");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("HelloFilter拦截了请求....");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("HelloFilter拦截了响应....");
}

@Override
public void destroy() {
System.out.println("HelloFilter被销毁....");
}
}

过滤器二:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HelloFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("HelloFilter2初始化....");
}

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("HelloFilter2拦截了请求....");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("HelloFilter2拦截了响应....");
}

@Override
public void destroy() {
System.out.println("HelloFilter2被销毁....");
}
}

web.xml中配置这两个过滤器及其过滤路径规则(也可以通过@WebFilter注解来配置):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>com.fxd.HelloServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>

<filter>
<filter-name>helloFilter</filter-name>
<filter-class>com.fxd.filter.HelloFilter</filter-class>
</filter>

<filter>
<filter-name>helloFilter2</filter-name>
<filter-class>com.fxd.filter.HelloFilter2</filter-class>
</filter>

<filter-mapping>
<filter-name>helloFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<filter-mapping>
<filter-name>helloFilter2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

这里将两个过滤器的路径都配置为/*,即拦截所有请求。将容器跑起来,可以看到过滤器被初始化,通过浏览器输入/hello路径访问helloServlet,可以看到请求先被第一个过滤器helloFilter拦截,然后被第二个过滤器helloFilter2拦截。关闭容器时,可以看到两个过滤器的销毁回调执行,下面是整个执行过程。

这里需要注意两个过滤器的拦截顺序,由web.xml配置文件中的标签顺序决定,也就是说,先配置的过滤器先对请求进行拦截,然后通过FilterChain将其放行至下一个过滤器或者web资源,某一个过滤器在处理请求时不调用FilterChain.doFilter()方法,则请求将在该过滤器终止。

FilterConfig接口

ServletConfig接口类似,FilterConfig接口用于在过滤器初始化期间传递消息。可以在init()方法中加入FilterConfig类型的参数,就能够获得该对象。

1
2
3
4
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println(filterConfig.getFilterName());
}

该接口一共定义了四个方法:

返回类型 方法名 描述
String getInitParameter(String name) 根据name获取该过滤器的初始化参数
Enumeration getInitParameterNames() 获取过滤器中所有初始化参数的name
ServletContext getServletContext() 获取ServletContext对象
String getFilterName() 返回过滤器的名称

Listener

Servlet中监听器Listener用于监听ServletContext,HttpSession以及ServletRequest对象的生命周期或属性的变化,当相应的事件发生时,会调用监听器相关的回调函数。

Servlet共规定了以下监听器接口:

  1. 监听对象创建和销毁的监听器:ServletContextListener,HttpSessionListener,ServletRequestListener
  2. 监听对象中属性变更的监听器:ServletContextAttributeListener,HttpSessionAttributeListener

ServletContextListener为例,实现该类型监听器时,需要实现该接口:

1
2
3
4
5
6
7
8
9
10
11
public class HelloListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {

}

@Override
public void contextDestroyed(ServletContextEvent sce) {

}
}

并将实现的监听器注册(也可以使用@WebListener注解实现):

1
2
3
<listener>
<listener-class>com.fxd.listener.HelloListener</listener-class>
</listener>

总结

本篇文章简要的介绍了Servlet中的过滤器和监听器的概念,其中过滤器应用比较广泛,当然在Spring中有拦截器用于实现类似的功能,还有AOP手段来实现横切式的编程。

参考文献

  1. C语言中文网Servlet教程