如何创建多个异步java过滤器?
Posted
技术标签:
【中文标题】如何创建多个异步java过滤器?【英文标题】:How to create multiple asynchronous java filters? 【发布时间】:2014-09-04 22:57:15 【问题描述】:我正在尝试创建一个具有多个异步过滤器的 Java 应用程序,但似乎无法让它们很好地协同工作。我认为主要问题在于 run() 方法,我不知道如何将请求传递给链中的下一个过滤器。我试过chain.doFilter(request, response)
,但这似乎不起作用,AsyncContext
上有dispatch()
和complete()
API,但它们似乎关闭了整个AsyncContext。似乎必须有另一种方法才能让它发挥作用。下面是我正在使用的过滤器的 sn-p - 第二个过滤器看起来几乎相同。
注意:我正在添加标头以尝试找出被调用的内容。
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException
final AsyncContext asyncContext = request.startAsync();
final HttpServletResponse res = (HttpServletResponse) response;
asyncContext.addListener(new AsyncListener()
@Override
public void onComplete(AsyncEvent event) throws IOException
res.addHeader("S-AST2", "onComplete");
@Override
public void onTimeout(AsyncEvent event) throws IOException
res.addHeader("S-AST3", "onTimeout");
@Override
public void onError(AsyncEvent event) throws IOException
res.addHeader("S-AST4", "onError");
@Override
public void onStartAsync(AsyncEvent event) throws IOException
res.addHeader("S-AST0", "onStartAsync");
);
asyncContext.start(new Runnable()
@Override
public void run()
res.addHeader("S-AST1", "before");
// This doesn't seem to work...
asyncContext.dispatch();
// ... or this ...
asyncContext.complete();
// ... or this ...
chain.doFilter(request, response);
);
感谢您的任何见解!
【问题讨论】:
【参考方案1】:我需要装饰响应,但我不知道底层 servlet 是否正在执行异步,或者它是否已经完成。在 Jetty 9.1.x 上,我通过期待 IllegalStateException
解决了它
以下示例说明了如何包装响应(使用自定义 BufferingHttpServletResponseWrapper
来缓冲所有写入响应的内容)以拦截输入,以便对其进行修饰。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// Buffer the output to a string in order to calculate its signature and add the signature to a header before it's sent to the client
final BufferingHttpServletResponseWrapper responseWrapper = new BufferingHttpServletResponseWrapper(httpServletResponse);
chain.doFilter(httpServletRequest, responseWrapper);
// This is the only way I can see that will safely let us know if we should treat this as an active async request or not.
try
httpServletRequest.getAsyncContext().addListener(new AsyncListener()
@Override
public void onComplete(AsyncEvent event) throws IOException
LOG.debug("onComplete ", event);
decorateResponse(responseWrapper);
@Override
public void onTimeout(AsyncEvent event) throws IOException
LOG.debug("onTimeout ", event);
@Override
public void onError(AsyncEvent event) throws IOException
LOG.debug("onError ", event);
@Override
public void onStartAsync(AsyncEvent event) throws IOException
LOG.debug("onStartAsync ", event);
event.getAsyncContext().addListener(this);
, httpServletRequest, responseWrapper);
LOG.debug("After chain.doFilter, async was started");
catch (IllegalStateException e)
LOG.debug("Async not active it appears... ", e.getMessage());
decorateResponse(responseWrapper);
【讨论】:
【参考方案2】:这个答案有两个部分。
1) chain.doFilter(request, response);
仍然是必需的。
2) 这不起作用的原因是,在每个过滤器和 servlet 中,我调用了 request.startAsync()
,它启动了一个 new 异步进程,而不是使用现有的进程。因此,如果过滤器启动了一个异步进程,并且 servlet 也启动了一个,它将覆盖/忽略在过滤器中启动的那个。要解决此问题,您必须通过调用request.isAsyncStarted()
检查是否已经启动了异步进程,如果是,则不应启动新的异步上下文,而应使用request.getAsyncContext()
获取现有的。下面是我为每个 servlet 和过滤器创建的帮助器类,这样我就可以调用 AsyncHelper.getAsyncContext(request, response)
,它会检索现有的 AsyncContext,或者创建一个新的。
public class AsyncHelper
public static AsyncContext getAsyncContext(ServletRequest request, ServletResponse response)
AsyncContext asyncContext = null;
if (request.isAsyncStarted())
asyncContext = request.getAsyncContext();
else
asyncContext = request.startAsync(request, response);
asyncContext.setTimeout(2000);
return asyncContext;
【讨论】:
阅读AsyncContext.isAsyncStarted
的 api-docs 看起来这个例子很容易导致 IllegalStateException
,这取决于 servlet 正在做什么;在对AsyncContext.isAsyncStarted
和AsyncContext.getAsyncContext
的调用之间,另一个线程可能通过调用AsyncContext.completed
或AsyncContext.dispatch
改变了AsyncContext
的状态以上是关于如何创建多个异步java过滤器?的主要内容,如果未能解决你的问题,请参考以下文章