每次请求只执行一次Filter, 不会重复执行
org.springframework.web.filter.OncePerRequestFilter
相关代码:
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.filter;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.request.async.WebAsyncManager;
import org.springframework.web.context.request.async.WebAsyncUtils;
import org.springframework.web.util.WebUtils;
/**
* Filter base class that aims to guarantee a single execution per request
* dispatch, on any servlet container. It provides a {@link #doFilterInternal}
* method with HttpServletRequest and HttpServletResponse arguments.
*
* As of Servlet 3.0, a filter may be invoked as part of a
* {@link javax.servlet.DispatcherType#REQUEST REQUEST} or
* {@link javax.servlet.DispatcherType#ASYNC ASYNC} dispatches that occur in
* separate threads. A filter can be configured in {@code web.xml} whether it
* should be involved in async dispatches. However, in some cases servlet
* containers assume different default configuration. Therefore sub-classes can
* override the method {@link #shouldNotFilterAsyncDispatch()} to declare
* statically if they should indeed be invoked, once, during both types
* of dispatches in order to provide thread initialization, logging, security,
* and so on. This mechanism complements and does not replace the need to
* configure a filter in {@code web.xml} with dispatcher types.
*
*
Subclasses may use {@link #isAsyncDispatch(HttpServletRequest)} to
* determine when a filter is invoked as part of an async dispatch, and use
* {@link #isAsyncStarted(HttpServletRequest)} to determine when the request
* has been placed in async mode and therefore the current dispatch won't be
* the last one for the given request.
*
*
Yet another dispatch type that also occurs in its own thread is
* {@link javax.servlet.DispatcherType#ERROR ERROR}. Subclasses can override
* {@link #shouldNotFilterErrorDispatch()} if they wish to declare statically
* if they should be invoked once during error dispatches.
*
*
The {@link #getAlreadyFilteredAttributeName} method determines how to
* identify that a request is already filtered. The default implementation is
* based on the configured name of the concrete filter instance.
*
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 06.12.2003
*/
public abstract class OncePerRequestFilter extends GenericFilterBean {
/**
* Suffix that gets appended to the filter name for the
* "already filtered" request attribute.
* @see #getAlreadyFilteredAttributeName
*/
public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED";
/**
* This {@code doFilter} implementation stores a request attribute for
* "already filtered", proceeding without filtering again if the
* attribute is already there.
* @see #getAlreadyFilteredAttributeName
* @see #shouldNotFilter
* @see #doFilterInternal
*/
@Override
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();
boolean hasAlreadyFilteredAttribute = request.getAttribute(alreadyFilteredAttributeName) != null;
if (hasAlreadyFilteredAttribute || skipDispatch(httpRequest) || 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);
}
}
}
private boolean skipDispatch(HttpServletRequest request) {
if (isAsyncDispatch(request) && shouldNotFilterAsyncDispatch()) {
return true;
}
if (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null && shouldNotFilterErrorDispatch()) {
return true;
}
return false;
}
/**
* The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced
* in Servlet 3.0 means a filter can be invoked in more than one thread over
* the course of a single request. This method returns {@code true} if the
* filter is currently executing within an asynchronous dispatch.
* @param request the current request
* @since 3.2
* @see WebAsyncManager#hasConcurrentResult()
*/
protected boolean isAsyncDispatch(HttpServletRequest request) {
return WebAsyncUtils.getAsyncManager(request).hasConcurrentResult();
}
/**
* Whether request processing is in asynchronous mode meaning that the
* response will not be committed after the current thread is exited.
* @param request the current request
* @since 3.2
* @see WebAsyncManager#isConcurrentHandlingStarted()
*/
protected boolean isAsyncStarted(HttpServletRequest request) {
return WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted();
}
/**
* Return the name of the request attribute that identifies that a request
* is already filtered.
*
The default implementation takes the configured name of the concrete filter
* instance and appends ".FILTERED". If the filter is not fully initialized,
* it falls back to its class name.
* @see #getFilterName
* @see #ALREADY_FILTERED_SUFFIX
*/
protected String getAlreadyFilteredAttributeName() {
String name = getFilterName();
if (name == null) {
name = getClass().getName();
}
return name + ALREADY_FILTERED_SUFFIX;
}
/**
* Can be overridden in subclasses for custom filtering control,
* returning {@code true} to avoid filtering of the given request.
*
The default implementation always returns {@code false}.
* @param request current HTTP request
* @return whether the given request should not be filtered
* @throws ServletException in case of errors
*/
protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return false;
}
/**
* The dispatcher type {@code javax.servlet.DispatcherType.ASYNC} introduced
* in Servlet 3.0 means a filter can be invoked in more than one thread
* over the course of a single request. Some filters only need to filter
* the initial thread (e.g. request wrapping) while others may need
* to be invoked at least once in each additional thread for example for
* setting up thread locals or to perform final processing at the very end.
*
Note that although a filter can be mapped to handle specific dispatcher
* types via {@code web.xml} or in Java through the {@code ServletContext},
* servlet containers may enforce different defaults with regards to
* dispatcher types. This flag enforces the design intent of the filter.
*
The default return value is "true", which means the filter will not be
* invoked during subsequent async dispatches. If "false", the filter will
* be invoked during async dispatches with the same guarantees of being
* invoked only once during a request within a single thread.
* @since 3.2
*/
protected boolean shouldNotFilterAsyncDispatch() {
return true;
}
/**
* Whether to filter error dispatches such as when the servlet container
* processes and error mapped in {@code web.xml}. The default return value
* is "true", which means the filter will not be invoked in case of an error
* dispatch.
* @since 3.2
*/
protected boolean shouldNotFilterErrorDispatch() {
return true;
}
/**
* Same contract as for {@code doFilter}, but guaranteed to be
* just invoked once per request within a single request thread.
* See {@link #shouldNotFilterAsyncDispatch()} for details.
*
Provides HttpServletRequest and HttpServletResponse arguments instead of the
* default ServletRequest and ServletResponse ones.
*/
protected abstract void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException;
}