Нужно ли вызывать .close () для HttpServletResponse.getOutputStream () /. GetWriter ()? - PullRequest
85 голосов
/ 21 июля 2009

Я не смог найти авторитетного ответа на этот вопрос с помощью Google. В Java-сервлетах можно получить доступ к телу ответа через response.getOutputStream () или response.getWriter (). Нужно ли вызывать .close () в этом потоке после того, как он был записан?

С одной стороны, есть призыв Блохиана всегда закрывать выходные потоки. С другой стороны, я не думаю, что в этом случае есть основной ресурс, который должен быть закрыт. Открытие / закрытие сокетов управляется на уровне HTTP, чтобы разрешить такие вещи, как постоянные соединения и тому подобное.

Ответы [ 5 ]

84 голосов
/ 21 июля 2009

Обычно вы не должны закрывать поток. Контейнер сервлета автоматически закроет поток после завершения работы сервлета в рамках жизненного цикла запроса сервлета.

Например, если вы закрыли поток, он не будет доступен, если вы внедрили фильтр .

Сказав все это, если вы закроете это, ничего плохого не произойдет, если вы не попытаетесь использовать его снова.

РЕДАКТИРОВАТЬ: другая ссылка фильтра

EDIT2: adrian.tarau правильно в том, что если вы хотите изменить ответ после того, как сервлет сделал свое дело, вы должны создать оболочку, расширяющую HttpServletResponseWrapper, и буферизовать вывод. Это позволяет предотвратить вывод выходных данных непосредственно клиенту, но также позволяет вам защитить, если сервлет закрывает поток, в соответствии с этим отрывком (выделено мной):

Фильтр, который изменяет ответ, должен обычно захватывает ответ перед ним возвращается клиенту. Путь к сделать это, чтобы передать сервлет, который генерирует ответ замену поток. Резервный поток предотвращает сервлет от закрытия оригинала поток ответа, когда он завершает и позволяет фильтру изменять ответ сервлета.

Статья

Из этой официальной статьи Sun можно сделать вывод, что закрытие выходного потока из сервлета является обычным явлением, но не обязательным.

67 голосов
/ 21 июля 2009

Общее правило для них таково: если вы открыли поток, то вы должны закрыть его.Если вы этого не сделали, вы не должны.Убедитесь, что код является симметричным.

В случае HttpServletResponse это немного менее ясно, так как не очевидно, если вызов getOutputStream() - это операция, которая открывает поток.Javadoc просто говорит, что это "Returns a ServletOutputStream";аналогично для getWriter().В любом случае ясно, что HttpServletResponse «владеет» потоком / записывающим устройством, и он (или контейнер) отвечает за его повторное закрытие.

Поэтому, чтобы ответить на ваш вопрос - нет, вы не должнызакройте поток в этом случае.Контейнер должен сделать это, и если вы попадете туда раньше, вы рискуете внести незначительные ошибки в ваше приложение.

4 голосов
/ 15 октября 2010

Если есть вероятность, что фильтр может быть вызван на «включенном» ресурсе, вам определенно следует , а не закрыть поток. Это приведет к сбою включающего ресурса с исключением «поток закрыт».

3 голосов
/ 19 февраля 2014

Еще один аргумент против закрытия OutputStream. Посмотри на этот сервлет. Это исключение. Исключение сопоставляется в файле web.xml с ошибкой JSP:

package ser;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;

@WebServlet(name = "Erroneous", urlPatterns = {"/Erroneous"})
public class Erroneous extends HttpServlet {

  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    resp.setContentType("text/html;charset=UTF-8");
    PrintWriter out = resp.getWriter();
    try {
      throw new IOException("An error");
    } finally {
//      out.close();
    }
  }
}

Файл web.xml содержит:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
    <session-config>
        <session-timeout>
            30
        </session-timeout>
    </session-config>
    <error-page>
        <exception-type>java.io.IOException</exception-type>
        <location>/error.jsp</location>
    </error-page>
</web-app>

И ошибка. Jsp:

<%@page contentType="text/html" pageEncoding="UTF-8" isErrorPage="true"%>
<!DOCTYPE html>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Error Page</title>
    </head>
    <body>
        <h1><%= exception.getMessage()%></h1>
    </body>
</html>

Когда вы загружаете /Erroneous в браузере, вы видите страницу с ошибкой, на которой отображается «Ошибка». Но если вы откомментируете строку out.close() в приведенном выше сервлете, повторно развернете приложение и перезагрузите /Erroneous, вы ничего не увидите в браузере. Я понятия не имею, что на самом деле происходит, но я предполагаю, что out.close() предотвращает обработку ошибок.

Протестировано с Tomcat 7.0.50, Java EE 6 с использованием Netbeans 7.4.

3 голосов
/ 21 июля 2009

Вы должны закрыть поток, код будет чище, так как вы вызываете getOutputStream (), и поток не передается вам в качестве параметра, когда обычно вы просто используете его и не пытаетесь закрыть его. Servlet API не утверждает, что если выходной поток может быть закрыт или не должен быть закрыт, в этом случае вы можете безопасно закрыть поток, любой контейнер позаботится о закрытии потока, если он не был закрыт сервлетом.

Вот метод close () в Jetty, они закрывают поток, если он не закрыт.

public void close() throws IOException
    {
        if (_closed)
            return;

        if (!isIncluding() && !_generator.isCommitted())
            commitResponse(HttpGenerator.LAST);
        else
            flushResponse();

        super.close();
    }

Также как разработчик фильтра вы не должны предполагать, что OutputStream не закрыт, вы всегда должны передавать другой OutputStream, если вы хотите изменить содержимое после того, как сервлет выполнил свою работу.

РЕДАКТИРОВАТЬ: Я всегда закрываю поток, и у меня не было никаких проблем с Tomcat / Jetty. Я не думаю, что у вас должны быть какие-либо проблемы с любым контейнером, старым или новым.

...