minitomcattomcat第九章:实现过滤器(Filter)和监听器(Listener)-Minitomcat
代长亚功能目标:
增加 Filter 和 Listener 支持,用于在请求处理过程中插入额外的操作或监听事件。
实现 Filter 接口,支持请求过滤,例如日志记录、认证拦截等功能。
实现 Listener 接口,支持监听 Servlet 上下文或会话的创建和销毁事件。
实现内容:
实现 Filter
接口,拦截 HTTP 请求并在请求前后插入自定义逻辑。
实现 Listener
接口,监听 Servlet 上下文、会话或请求的生命周期事件。
实现一个简单的日志过滤器,记录每个请求的访问时间和路径。
示例功能:
9.1 过滤器(Filter)
Filter
是 Servlet 容器提供的一个接口,它允许开发者在请求到达 Servlet 之前和响应返回客户端之前插入自定义的处理逻辑。过滤器可以用于执行常见的任务,如日志记录、权限验证、请求重定向、输入输出数据处理等。
1. Filter
接口的实现
Filter
接口提供了三个方法:
init(FilterConfig config)
:初始化过滤器。
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
:执行过滤器的核心逻辑,并将请求传递给下一个过滤器或目标 Servlet。
destroy()
:销毁过滤器。
2. 日志过滤器的实现
我们实现一个简单的日志过滤器,用于记录每个请求的访问时间和请求路径。
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 30 31 32
| 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) throws ServletException { }
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { long startTime = System.currentTimeMillis(); HttpServletRequest request = (HttpServletRequest) servletRequest; System.out.println("Request started at: " + startTime + " for path: " + request.getRequestURI());
filterChain.doFilter(servletRequest, servletResponse);
long endTime = System.currentTimeMillis(); System.out.println("Request completed at: " + endTime + " for path: " + request.getRequestURI() + " Took: " + (endTime - startTime) + "ms"); }
@Override public void destroy() { } }
|
在 LogFilter
中,doFilter()
方法记录了请求的访问时间和请求路径,并通过 chain.doFilter()
将请求传递给下一个过滤器或目标 Servlet。过滤器结束后,我们记录了请求处理的结束时间及耗时。
3. FilterChain
接口的实现
在实现 FilterChain
时,我们需要设计一个链式调用结构,它能够依次调用过滤器列表中的每个 Filter
,直到最后一个过滤器或者目标 Servlet 被调用。FilterChain
的具体实现会将请求和响应对象传递给过滤器,并在每个过滤器执行完之后,继续执行下一个过滤器或最终的 Servlet 处理。
下面是一个具体的 FilterChain
实现:
FilterChain 设计概念
FilterChain
是一个责任链模式的实现,每个过滤器执行完后,调用链中的下一个过滤器。
doFilter
方法将在链中的每个过滤器之间传递请求和响应对象。
一旦所有过滤器执行完,FilterChain
会将请求传递给实际的 Servlet 处理。
FilterChain 代码实现
FilterChain 接口
1 2 3 4
| public interface FilterChain { void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException; }
|
FilterChain 实现类
我们需要实现 FilterChain
接口,并在其内部维护过滤器列表。每次调用 doFilter
方法时,会依次执行过滤器链中的过滤器,直到最终的 Servlet 被调用。
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 30 31 32 33 34
| package com.daicy.minitomcat.servlet;
import com.google.common.collect.Lists;
import javax.servlet.*; import java.io.IOException; import java.util.LinkedList; import java.util.List;
public class FilterChainImpl implements FilterChain {
private List<Filter> filters; private int currentIndex = 0;
public FilterChainImpl(List<Filter> filters) { this.filters = filters; }
@Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { if (currentIndex < filters.size()) { Filter filter = filters.get(currentIndex); currentIndex++; filter.doFilter(request, response, this); } } }
|
FilterManager 代码
FilterManager
负责创建过滤器链,并传递请求和响应给链中的过滤器。它将 FilterChain
作为一个链式调用传递给每个过滤器。
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
| package com.daicy.minitomcat;
import com.daicy.minitomcat.servlet.FilterChainImpl;
import javax.servlet.*; import java.io.IOException; import java.util.*;
public class FilterManager { private List<Filter> filters = new ArrayList<>();
public void addFilter(Filter filter) { filters.add(filter); }
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterChain defaultChain = new FilterChainImpl(filters); defaultChain.doFilter(request, response); }
public List<Filter> getFilters() { return filters; } }
|
9.2 监听器(Listener)
Listener
是 Servlet 容器提供的另一种机制,用于监听 Servlet 容器内的特定事件。监听器用于捕捉和响应 Servlet 上下文、会话或请求的生命周期变化。例如,可以用来监听会话的创建和销毁,或者在应用启动时初始化共享资源。
1. ServletContextListener
接口
ServletContextListener
接口用于监听 Servlet 上下文的生命周期事件。当 Servlet 容器启动或销毁时,会触发对应的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package com.daicy.minitomcat;
import javax.servlet.*;
public class ServletContextListenerImpl implements ServletContextListener {
@Override public void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); System.out.println("Servlet context initialized."); servletContext.setAttribute("initialized", true); }
@Override public void contextDestroyed(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.getServletContext(); System.out.println("Servlet context destroyed."); servletContext.removeAttribute("initialized"); } }
|
contextInitialized()
方法在 Servlet 上下文初始化时调用,contextDestroyed()
方法在 Servlet 上下文销毁时调用。这里我们简单打印了日志,表示 Servlet 上下文的生命周期。
2. HttpSessionListener
接口
HttpSessionListener
接口用于监听 HTTP 会话的创建和销毁。当用户的会话创建或销毁时,会触发对应的事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 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) { HttpSession session = httpSessionEvent.getSession(); System.out.println("Session created with ID: " + session.getId()); session.setAttribute("created", true); }
@Override public void sessionDestroyed(HttpSessionEvent httpSessionEvent) { HttpSession session = httpSessionEvent.getSession(); System.out.println("Session destroyed with ID: " + session.getId()); session.removeAttribute("created"); } }
|
sessionCreated()
方法在会话创建时调用,sessionDestroyed()
方法在会话销毁时调用。
3. 注册监听器 ServletContextListenerManager
,HttpSessionListenerManager
1 2
| servletContextListenerManager.addListener(new ServletContextListenerImpl()); sessionListenerManager.addListener(new HttpSessionListenerImpl());
|
上述配置分别监听 Servlet 上下文和 HTTP 会话的生命周期事件。
9.3 学习收获
通过实现 Filter 和 Listener,我们深入理解了 Servlet 规范中的两大重要机制:过滤器和监听器。具体来说:
这两者的结合,使得我们能够对请求处理流程进行灵活的扩展和控制,并实现更加复杂和丰富的功能。
这样,我们成功地实现了一个基本的过滤器和监听器机制,可以在 Servlet 容器中进行请求过滤和事件监听。这对于开发者来说,是一个非常强大的扩展点,能够让我们轻松地插入横切逻辑,如日志、权限验证、性能监控等,同时也能方便地管理应用生命周期事件。
项目源代码地址:
https://github.com/daichangya/MiniTomcat/tree/chapter9/mini-tomcat