2009-07-21 1 views
75

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

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

ответ

77

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

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

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

EDIT: another filter link

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

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

Article

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

+2

Это правильно. Следует отметить, что в некоторых случаях вам может понадобиться очистить поток, и это вполне допустимо. – toluju

+1

Существует еще один побочный эффект закрытия писателя. Вы также не сможете установить код состояния через response.setStatus после его закрытия. –

+1

Следуйте этому совету. Это сэкономит вам много боли. Я бы не flush(), если вы не знаете, почему вы это делаете - вы должны позволить буферизации дескриптора контейнера. – Hal50000

3

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

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

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

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

     super.close(); 
    } 

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

EDIT: Я всегда закрываю поток, и у меня не было проблем с Tomcat/Jetty.Я не думаю, что у вас возникнут проблемы с контейнером, старым или новым.

+4

«Вы должны закрыть поток, код чище ...» - для меня код, напертый с .close() выглядит менее чистым, чем код без, особенно если .close() не требуется - вот что это за вопрос пытается определить. –

+1

yep, но красота приходит после :) В любом случае, поскольку API не ясен, я бы предпочел закрыть его, код выглядит согласованным, как только вы запросите OutputStream, вы должны закрыть его, если API не говорит «не закрывайте его» ». –

+0

Я не думаю, что закрытие потока вывода в моем собственном коде является хорошей практикой. Это задача контейнера. – JimHawkins

58

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

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

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

+0

Я согласен с этим ответом, вы также можете проверить ServletResponse.flushBuffer(): http://docs.oracle.com/javaee/1.4/api/javax/servlet/ServletResponse.html#flushBuffer() –

+5

" если вы открыли поток, то вы должны закрыть его.Если вы этого не сделали, вы не должны "--- хорошо сказано – SrikanthLingala

+2

Похоже на предложения, опубликованные на школьной доске," если вы откроете его, закройте его. Если вы включите его, выключите его. Если вы разблокируете его, заблокируйте его. [...] " – Ricardo

4

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

+0

Спасибо, что добавили этот комментарий. Как ни странно, я все еще немного разбираюсь с проблемой - только сейчас я заметил, что в сервлет-шаблоне NetBeans включен код для закрытия выходного потока ... –

2

Еще один аргумент против закрытия 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> 

И error.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.