第九章:实现过滤器(Filter)和监听器(Listener)-Minitomcat
第九章:实现过滤器(Filter)和监听器(Listener)
本章目标
本章旨在为 Minitomcat 容器增加 Filter(过滤器) 和 Listener(监听器) 的支持,以便在请求处理过程中插入额外的操作或监听特定事件。具体实现目标如下:
- 实现 Filter 接口:支持在请求到达 Servlet 之前和响应返回客户端之前插入自定义逻辑,例如日志记录、认证拦截等。
- 实现 Listener 接口:支持监听 Servlet 上下文(Context)、会话(Session)或请求的生命周期事件。
示例功能:
- 实现一个日志过滤器,记录每个请求的访问时间和路径。
- 在 Servlet 上下文初始化时创建监听器,监听上下文和会话的创建与销毁事件。
9.1 过滤器(Filter)
Filter 是 Servlet 容器提供的一个接口,它允许开发者在请求到达 Servlet 之前和响应返回客户端之前插入自定义的处理逻辑。过滤器常用于执行日志记录、权限验证、请求重定向、输入输出数据处理等通用任务。
1. Filter 接口的实现
标准的 Filter 接口主要包含三个方法:
init(FilterConfig config.html):初始化过滤器。doFilter(ServletRequest request, ServletResponse response, FilterChain chain.html):执行过滤器的核心逻辑,并将请求传递给下一个过滤器或目标 Servlet。destroy():销毁过滤器,释放资源。
2. 日志过滤器的实现
下面实现一个简单的日志过滤器,用于记录每个请求的访问时间和请求路径。
package com.daicy.minitomcat;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Date;
public class LoggingFilter implements Filter {
@Override
public void init(FilterConfig filterConfig.html) throws ServletException {
// 初始化操作,如果有需要可以在这里读取配置参数等
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain.html) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
HttpServletRequest request = (HttpServletRequest.html) servletRequest;
System.out.println("Request started at: " + startTime + " for path: " + request.getRequestURI(.html));
// 将请求传递给下一个过滤器或目标 Servlet
filterChain.doFilter(servletRequest, servletResponse.html);
long endTime = System.currentTimeMillis();
System.out.println("Request completed at: " + endTime + " for path: " + request.getRequestURI(.html) + " Took: " + (endTime - startTime.html) + "ms");
}
@Override
public void destroy() {
// 清理资源操作
}
}在 LoggingFilter 中,doFilter() 方法记录了请求的开始时间、路径,并通过 chain.doFilter() 将请求传递给链中的下一个节点。过滤器执行完毕后,记录结束时间及总耗时。
3. FilterChain 接口的实现
为了实现过滤器的链式调用,我们需要设计一个 FilterChain 结构。它能够依次调用过滤器列表中的每个 Filter,直到最后一个过滤器或者目标 Servlet 被调用。
FilterChain 设计概念
FilterChain是责任链模式的实现,每个过滤器执行完后,调用链中的下一个过滤器。doFilter方法将在链中的每个过滤器之间传递请求和响应对象。- 一旦所有过滤器执行完,
FilterChain会将请求传递给实际的 Servlet 处理。
FilterChain 代码实现
首先定义接口:
public interface FilterChain {
// 执行链中的下一个过滤器或目标处理
void doFilter(ServletRequest request, ServletResponse response.html) throws IOException, ServletException;
}接下来是实现类。我们需要在内部维护过滤器列表,每次调用 doFilter 方法时,依次执行过滤器链中的过滤器,直到最终的 Servlet 被调用。
package com.daicy.minitomcat.servlet;
import javax.servlet.*;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
/**
* @author: create by daichangya
* @version: v1.0
* @description: com.daicy.minitomcat.servlet
* @date: 19-11-12
*/
public class FilterChainImpl implements FilterChain {
private List<Filter> filters; // 存储过滤器的列表
private int currentIndex = 0; // 当前过滤器的索引
public FilterChainImpl(List<Filter> filters.html) {
this.filters = filters;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response.html) throws IOException, ServletException {
// 如果当前索引小于过滤器链的长度,执行下一个过滤器
if (currentIndex < filters.size(.html)) {
Filter filter = filters.get(currentIndex.html);
currentIndex++; // 递增索引,指向下一个过滤器
filter.doFilter(request, response, this.html); // 递归调用,传入下一个过滤器
}
}
}4. FilterManager 代码
FilterManager 负责创建过滤器链,并传递请求和响应给链中的过滤器。它将 FilterChain 作为一个链式调用对象传递给每个过滤器。
package com.daicy.minitomcat;
import com.daicy.minitomcat.servlet.FilterChainImpl;
import javax.servlet.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class FilterManager {
private List<Filter> filters = new ArrayList<>();
// 添加过滤器
public void addFilter(Filter filter.html) {
filters.add(filter.html);
}
// 执行过滤器链
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain.html) throws IOException, ServletException {
FilterChain defaultChain = new FilterChainImpl(filters.html);
defaultChain.doFilter(request, response.html); // 递归执行过滤器链
}
public List<Filter> getFilters() {
return filters;
}
}9.2 监听器(Listener)
Listener 是 Servlet 容器提供的另一种机制,用于监听容器内的特定事件。监听器用于捕捉和响应 Servlet 上下文、会话或请求的生命周期变化。例如,可以用来监听会话的创建和销毁,或者在应用启动时初始化共享资源。
1. ServletContextListener 接口
ServletContextListener 接口用于监听 Servlet 上下文的生命周期事件。当 Servlet 容器启动或销毁时,会触发对应的事件。
package com.daicy.minitomcat;
import javax.servlet.*;
public class ServletContextListenerImpl implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent.html) {
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println("Servlet context initialized.".html);
// 在这里可以进行初始化操作,如加载配置文件等
servletContext.setAttribute("initialized", true.html);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent.html) {
ServletContext servletContext = servletContextEvent.getServletContext();
System.out.println("Servlet context destroyed.".html);
// 在这里可以进行资源清理操作,如关闭数据库连接池等
servletContext.removeAttribute("initialized".html);
}
}contextInitialized() 方法在 Servlet 上下文初始化时调用,contextDestroyed() 方法在 Servlet 上下文销毁时调用。上述代码简单打印了日志,表示 Servlet 上下文的生命周期状态。
2. HttpSessionListener 接口
HttpSessionListener 接口用于监听 HTTP 会话的创建和销毁。当用户的会话创建或销毁时,会触发对应的事件。
package com.daicy.minitomcat;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
public class HttpSessionListenerImpl implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent.html) {
HttpSession session = httpSessionEvent.getSession();
System.out.println("Session created with ID: " + session.getId(.html));
// 可以在这里进行与新会话相关的初始化操作,如创建会话相关的缓存
session.setAttribute("created", true.html);
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent.html) {
HttpSession session = httpSessionEvent.getSession();
System.out.println("Session destroyed with ID: " + session.getId(.html));
// 可以在这里清理与该会话相关的资源
session.removeAttribute("created".html);
}
}sessionCreated() 方法在会话创建时调用,sessionDestroyed() 方法在会话销毁时调用。
3. 注册监听器
我们需要通过管理器注册实现的监听器,以便容器能够触发相应的事件。
servletContextListenerManager.addListener(new ServletContextListenerImpl(.html));
sessionListenerManager.addListener(new HttpSessionListenerImpl(.html));上述配置分别监听 Servlet 上下文和 HTTP 会话的生命周期事件。
9.3 学习收获
通过实现 Filter 和 Listener,我们深入理解了 Servlet 规范中的两大重要机制:
- Filter(过滤器):允许我们在请求处理链中插入额外的处理逻辑,如日志记录、认证验证、数据修改等。通过实现
Filter接口,我们能够自定义请求处理的前后逻辑。 - Listener(监听器):使我们能够监听 Servlet 上下文、会话和请求的生命周期事件。通过实现
Listener接口,我们能够在特定的生命周期阶段(如应用启动、会话创建等)执行特定的逻辑。
这两者的结合,使得我们能够对请求处理流程进行灵活的扩展和控制,并实现更加复杂和丰富的功能。这对于开发者来说,是一个非常强大的扩展点,能够让我们轻松地插入横切逻辑(如日志、权限验证、性能监控等),同时也能方便地管理应用生命周期事件。
项目源代码地址:
https://github.com/daichangya/MiniTomcat/tree/chapter9/mini-tomcat
说明:本文代码基于javax.servlet包(Servlet 3.x/4.x 规范)。若使用 Jakarta EE 9 及以上版本,包名已变更为jakarta.servlet,请注意兼容性问题。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。