Чи повинен один виклик .close () на HttpServletResponse.getOutputStream () /. GetWriter ()?


96

У сервітах Java можна отримати доступ до тіла відповіді через response.getOutputStream()або response.getWriter(). Чи слід звертатися .close()до цього OutputStreamпісля того, як це було написано?

З одного боку, є заклик Блохіану завжди закривати OutputStreams. З іншого боку, я не думаю, що в цьому випадку існує базовий ресурс, який потрібно закрити. Відкриття / закриття сокетів управляється на рівні HTTP, щоб дозволити такі речі, як постійні підключення тощо.


Вас не запрошують здогадуватися про те, чи є базовий ресурс, який потрібно закрити. Якщо реалізатор так думає, чи , вірніше , знає так, він забезпечить , close()що нічого не робить. Що ви повинні зробити, це закрити кожен доступний ресурс.
Маркіз

1
Навіть якщо ваш код не відкрив його? Я не думаю ...
Стівен Гувіг,

Відповіді:


93

Зазвичай ви не повинні закривати потік. Контейнер сервлета автоматично закриє потік після закінчення запуску сервлета як частини життєвого циклу запиту сервлета.

Наприклад, якщо ви закрили потік, він не буде доступний, якщо ви застосуєте фільтр .

Сказавши все це, якщо ви закриєте це, нічого поганого не станеться до тих пір, поки ви не спробуєте використовувати його знову.

EDIT: ще одне посилання на фільтр

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

Фільтр, який модифікує відповідь, зазвичай повинен фіксувати відповідь, перш ніж її повернути клієнту. Спосіб зробити це - передати сервлет, який генерує відповідь, потоком очікування. Потік очікування не дозволяє сервлету закривати вихідний потік відповідей, коли він завершується, і дозволяє фільтру змінювати відповідь сервлета.

Стаття

З цієї офіційної статті Sun можна зробити висновок, що закриття OutputStreamсервлета - це звичайне явище, але не є обов'язковим.


2
Це вірно. Варто зазначити, що в деяких випадках вам може знадобитися промити потік, і це цілком допустимо.
toluju

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

1
Дотримуйтесь цієї поради. Це позбавить вас багато болю. Я б не промивав (), якщо ви не знаєте, чому це робите - ви повинні дозволити контейнеру обробляти буферизацію.
Hal50000

75

Загальне їх правило таке: якщо ви відкрили потік, то вам слід його закрити. Якщо ви цього не зробили, ви не повинні. Переконайтесь, що код симетричний.

У випадку HttpServletResponseце трохи менш чіткий розріз, оскільки не очевидно, якщо виклик getOutputStream()- це операція, яка відкриває потік. Javadoc просто каже, що це " Returns a ServletOutputStream"; аналогічно для getWriter(). У будь-якому випадку зрозумілим є те, що HttpServletResponse"належить" потоку / записувача, і він (або контейнер) відповідає за його повторне закриття.

Отже, щоб відповісти на ваше запитання - ні, у цьому випадку ви не повинні закривати потік. Контейнер повинен це зробити, і якщо ви потрапите туди до нього, ви ризикуєте ввести непомітні помилки у вашій програмі.


Я погоджуюся з цією відповіддю, ви також можете перевірити ServletResponse.flushBuffer () див.: Docs.oracle.com/javaee/1.4/api/javax/servlet/…
cyber-monk

14
"Якщо ви відкрили потік, тоді його слід закрити. Якщо цього не зробили, ви не повинні" --- добре сказано
Шрікант Редді Лінгала

2
Схоже, що пропозиції, розміщені на шкільній дошці, "якщо ви відкриєте її, закрийте її. Якщо ви ввімкніть, вимкніть її. Якщо ви розблокуєте, заблокуйте. [...]"
Рікардо

Я помітив, що я використовую потік на початку свого коду і ніколи більше, клієнт чекає виконання всього сервлета, але коли я дзвоню, close()коли я закінчує цей потік, клієнт повертається негайно, а решта сервлета продовжує виконувати. Хіба це не зробить відповідь дещо відноснішою? на відміну від остаточного так чи ні
BiGGZ

"Якщо ви відкрили потік, тоді його слід закрити. Якщо цього не зробили, ви не повинні. Переконайтеся, що код симетричний." - Що робити, якщо потім створити інший потік, який завершує цей потік? Важко залишатися симетричним у цьому випадку, оскільки виклик закрити зовнішній потік зазвичай закриває вкладений.
steinybot

5

Якщо є ймовірність, що фільтр може бути викликаний на ресурсі "включений", вам точно не слід закривати потік. Це призведе до виходу з ладу ресурсу, що включає, за винятком "потоку закрито".


Дякуємо за додавання цього коментаря. Досить весело, я все ще трохи стикаюся з проблемою - тільки що я помітив шаблон сервлетів NetBeans, що включає код для закриття вихідного потоку ...
Стівен Ювіг,

4

Вам слід закрити потік, код чистіший, оскільки ви викликаєте getOutputStream () і потік не передається вам як параметр, коли зазвичай ви просто використовуєте його і не намагаєтесь його закрити. API сервлетів не зазначає, що якщо вихідний потік може бути закритим або не повинен бути закритим, у цьому випадку ви можете безпечно закрити потік, будь-який контейнер там піклується про закриття потоку, якщо він не був закритий сервлетом.

Ось метод 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
так, але краса приходить після :) У будь-якому випадку, оскільки API незрозумілий, я вважаю за краще закрити його, код виглядає послідовним, як тільки ви запитаєте OutputStream, ви повинні закрити його, якщо API не каже "не закривати його".
adrian.tarau

Я не вважаю, що закриття вихідного потоку у власному коді є хорошою практикою. Оце робота контейнера.
JimHawkins

get * не створює потік, це не його виробник. Не слід його закривати, контейнер повинен це робити.
dmatej

3

Ще один аргумент проти закриття OutputStream. Подивіться на цю серветку. Це кидає виняток. Виняток відображається у веб.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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.