【Spring深入解读】OncePerRequestFilter

本文详细介绍了Spring框架中OncePerRequestFilter的工作原理。该类通过对请求进行标记,确保每个HTTP请求仅被过滤一次,避免重复过滤的问题。具体实现上,通过设置一个标志属性来判断请求是否已被处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们发现Spring提供的URL请求Filter,都继承了OncePerRequestFilter类,

这个类是对原生的Filter进行优化,使Filter对请求不会循环调用,而是只调用一次,循环调用的将被忽略。

我们看一下源码:

/**
	 * This <code>doFilter</code> implementation stores a request attribute for
	 * "already filtered", proceeding without filtering again if the
	 * attribute is already there.
	 * @see #getAlreadyFilteredAttributeName
	 * @see #shouldNotFilter
	 * @see #doFilterInternal
	 */
	public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
			throws ServletException, IOException {

		if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
			throw new ServletException("OncePerRequestFilter just supports HTTP requests");
		}
		HttpServletRequest httpRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;

		String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
		if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {
			// Proceed without invoking this filter...
			filterChain.doFilter(request, response);
		}
		else {
			// Do invoke this filter...
			request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
			try {
				doFilterInternal(httpRequest, httpResponse, filterChain);
			}
			finally {
				// Remove the "already filtered" request attribute for this request.
				request.removeAttribute(alreadyFilteredAttributeName);
			}
		}
	}

这个代码里面,OncePerRequestFilter实现了doFilter方法,并在过滤时,加入alreadyFilteredAttributeName这个属性,通过这个值,判断是不是循环调用了。

### SpringOncePerRequestFilter 的使用方法 OncePerRequestFilterSpring 提供的一个抽象类,用于确保过滤器逻辑仅执行一次,即使在一个请求中有多个内部转发的情况也只触发一次[^1]。 #### 特点说明 此类继承自 `HttpServletFilter` 并实现了 `javax.servlet.Filter` 接口。它通过重写 doFilterInternal 方法来定义具体的业务逻辑,在此之前会先调用 shouldNotFilter 来判断当前请求是否应该被忽略处理[^2]。 #### 实现方式 为了创建自己的过滤器并利用 OncePerRequestFilter 功能,开发者需扩展该类,并实现其中的 abstract 方法: - **doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)**: 定义实际要做的工作。 - **shouldNotFilter(HttpServletRequest request)** (可选): 返回 true 表示不应用此过滤器,默认情况下总是返回 false 即始终生效[^3]。 下面是一个简单的例子展示如何基于 OncePerRequestFilter 构建一个日志记录过滤器: ```java import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class LoggingFilter extends OncePerRequestFilter { @Override protected void doFilterInternal( HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException { String clientIp = getClientIpAddress(httpServletRequest); logger.info("Handling HTTP Request from " + clientIp); long startTimeMillis = System.currentTimeMillis(); try { // 继续链中的下一个元素 filterChain.doFilter(httpServletRequest, httpServletResponse); } finally { long endTimeMillis = System.currentTimeMillis(); logger.info(String.format("Finished handling HTTP request after %d ms", (endTimeMillis - startTimeMillis))); } } } ``` 上述代码片段展示了怎样构建一个名为LoggingFilter的日志记录工具,它可以打印出每次HTTP请求的时间戳以及客户端IP地址信息[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值