#java #asynchronous #tomcat #filter
Вопрос:
Например, название
@WebFilter("/*")
@Component
@Slf4j
public class FilterDemo1 implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException, IOException {
new Thread(() -> {
try {
chain.doFilter(req, resp);
} catch (Exception e) {
log.error("a", e);
}
}).start();
}
}
Поэтому, если в фильтре Tomcat есть трудоемкие задачи (такие как RPC или HTTP), они должны ждать и не могут быть асинхронными
java.lang.NullPointerException: null
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) ~[tomcat-embed-core-9.0.27.jar:9.0.27]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.27.jar:9.0.27]```
[error][1]
[1]: https://i.stack.imgur.com/tFYTH.png
Ответ №1:
Объект ServletRequest
, ServletResponse
и FilterChain
действителен только в потоке, в котором doFilter
вызывается. Если вы хотите использовать их асинхронно, вам необходимо включить асинхронную обработку (см. Руководство по Jakarta EE).
ServletRequest.startAsync()
переводит ServletRequest
и ServletResponse
в асинхронный режим, но FilterChain
может использоваться только в исходном потоке:
service
Метод должен выполняться в том же потоке, что и все фильтры, применяемые к сервлету.
(см. Спецификацию сервлета)
Поэтому вам нужно действовать следующим образом:
- Когда новый запрос проходит через фильтр, вы вызываете
ServletRequest.startAsync()
и запускаете свой новый поток или используете любого другого исполнителя для асинхронной фильтрации (напримерAsyncContext.start(Runnable)
), - Когда асинхронная задача будет завершена, вы запишете результаты в качестве атрибутов запроса и вызовете
AsyncContext.dispatch()
: это перезапустит цепочку фильтров с самого начала, - При
doFilter
повторном вызове вы используете атрибуты запроса для выполнения логики фильтрации и вызоваFilterChain.doFilter
Например, вы можете использовать что-то вроде этого:
@WebFilter(asyncSupported = true, urlPatterns = {"/*"}, dispatcherTypes = {DispatcherType.ASYNC, DispatcherType.REQUEST})
public class Filter1 implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
switch (request.getDispatcherType()) {
case REQUEST :
// First pass: start asynchronous processing
final AsyncContext asyncContext = request.startAsync();
new Thread(() -> {
try {
Thread.currentThread().sleep(5000);
} catch (Exception e) {
request.setAttribute("filter1Error", e);
}
asyncContext.dispatch();
}).start();
break;
case ASYNC :
// Second pass: throw or forward
Exception e = (Exception) request.getAttribute("filter1Error");
if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof ServletException) {
throw (ServletException) e;
} else if (e != null) {
throw new ServletException(e);
}
chain.doFilter(request, response);
break;
default :
}
}