DoFilter называется дважды, предполагаемое поведение?
Я работаю над учебником по сервлету Java EE и пробовал пример настроения. Я заметил, что doFilter получает вызов дважды, как только вызов сервлета находится в цепочке, а во второй раз это не так.
Я добавил несколько printlns в TimeOfDayFilter.java и в MoodServlet.java, чтобы показать это.
TimeOfDayFilter.java:
...
System.out.println("TimeOfDay before"); //added
chain.doFilter(req, res);
System.out.println("TimeOfDay after"); //added
...
MoodServlet.java:
...
response.setContentType("text/html;charset=UTF-8");
System.out.println("MoodServlet"); //added
PrintWriter out = response.getWriter();
...
Результат от окна сервера Glassfish (3.1) при вызове сервлета выглядит следующим образом:
INFO: mood was successfully deployed in 406 milliseconds.
INFO: TimeOfDay before
INFO: MoodServlet
INFO: TimeOfDay after
INFO: TimeOfDay before
INFO: TimeOfDay after
Является ли это предполагаемым поведением? Если да, в чем причина дополнительного вызова?
Ответы
Ответ 1
chain.doFilter(request,response);
Это приведет к управлению сервлетом, с которым связан фильтр.
Но после выполнения соответствующего сервлета элемент управления возвращается в конце указанной строки, и все строки после этого в текущем doFilter() выполняются.
Если вы хотите передать управление на сервлет и не дать ему вернуться в фильтр, просто добавьте
return;
в конце строки chain.doFilter(запрос, ответ) в текущем фильтре.
Ответ 2
Метод Filter.doFilter
вызывается один раз для каждого запроса. Вы можете выполнить некоторый код до вызова других фильтров в цепочке, а также после этого (в порядке, указанном в цепочке фильтров, согласно порядку web.xml filter-mapping
), что-то вроде следующего примера:
public MyFilter implements Filter {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException
{
codeToExecuteBeforeOtherFiltersInTheChain(request, response);
chain.doFilter(request, response);
codeToExecuteAfterOtherFiltersInTheChain(request, response);
}
}
Если ваш фильтр настроен на отправку запросов REQUEST
и FORWARD
, то метод MyFilter.doFilter
будет вызываться один раз для исходного запроса и один раз, если запрос был перенаправлен:
Настройте сопоставление фильтра с помощью файла web.xml
:
...
<filter-mapping>
<filter-name>MyFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
...
Настройте сопоставление фильтров с помощью аннотации @WebFilter:
@WebFilter(urlPatterns = "/*", dispatcherTypes = {
DispatcherType.REQUEST, DispatcherType.FORWARD
}) public MyFilter implements Filter {
...
}
Чтобы иметь возможность проверить, был ли запрос перенаправлен, вы можете использовать атрибут запроса, описанный здесь: Как узнать, когда запрос переадресован в объекте RequestWrapper
Подробнее о фильтрах см. Https://docs.oracle.com/cd/B32110_01/web.1013/b28959/filters.htm.
Ответ 3
Да, это так, что фильтр выполняется дважды в жизненном цикле, сначала звонит, когда запрос от клиента приходит в сервлет и второй раз, когда ответ отправляется клиенту после выполнения сервлета.
Порядок выполнения каким-то образом выглядит следующим образом.
![Жизненный цикл фильтра]()
Ответ 4
Причина, по которой фильтр вызывается дважды, - это изображения, используемые в создании ответа, например,
out.println("<img src=\"resources/images/duke.snooze.gif\" alt=\"Duke sleeping\"/><br/>");
См. вывод журнала
2016-01-16T11:25:34.894+0100|Info: TimeOfDay doFilter method before sending to chain
2016-01-16T11:25:34.895+0100|Info: MoodServlet get method called
2016-01-16T11:25:34.895+0100|Info: TimeOfDay doFilter method after sending to chain
2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method before sending to chain
2016-01-16T11:25:34.942+0100|Info: TimeOfDay doFilter method after sending to chain
src в теге img - это не что иное, как второй запрос для сервера. Обратите внимание на шаблон URL, используемый в @WebFilter
@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/*"},
initParams = {
@WebInitParam(name = "mood", value = "awake")})
Он перехватит все запросы, поступающие в приложение для настроения. В качестве упражнения просто попробуйте удалить изображения из ответа или изменить шаблон url для перехвата только запросов, заканчивающихся в MoodServlet
@WebFilter(filterName = "TimeOfDayFilter",
urlPatterns = {"/report"},
initParams = {
@WebInitParam(name = "mood", value = "awake")})
Оба будут приводить к одному вызову doFilter, как вы ожидали раньше.
2016-01-16T11:28:53.485+0100|Info: TimeOfDay doFilter method before sending to chain
2016-01-16T11:28:53.486+0100|Info: MoodServlet get method called
2016-01-16T11:28:53.487+0100|Info: TimeOfDay doFilter method after sending to chain
Ответ 5
Я сталкивался с той же проблемой, когда doFilter
вызывается дважды (или несколько раз). Проблема заключалась в том, что фильтр обрабатывал каждый запрос, включая css, js, image и все другие файлы, в то время как я ожидал один запрос для каждой страницы, поэтому я решил эту проблему, добавив следующий код:
@WebFilter(filterName = "MyCustomFilter")
public class MyCustomFilter implements Filter {
public void doFilter(ServletRequest request,ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String accept = httpRequest.getHeader("accept");
// Since the filter handles every request
// we have to ensure that the request is asking for text/html
if (accept == null || !accept.toLowerCase().startsWith("text/html")) {
chain.doFilter(request, response);
return;
}
// your code goes here
Надеюсь, что это поможет таким людям, как я, которые погуглили этот вопрос.
Ответ 6
Как сказал Мохан, @Component заставит ваш фильтр вызываться дважды, если он уже зарегистрирован в вашем классе Application, например:
resources.add(new MyFilter());
В этом случае вы должны выбрать между аннотированием или регистрацией. Но это действительно только для приложений JAX-RS, которые используют Spring. Не тема этого вопроса.
Ответ 7
Я решил ту же проблему после удаления @Component в классе CustomFilter.