W Java Servlets można uzyskać dostęp do treści odpowiedzi za pośrednictwem response.getOutputStream()
lub response.getWriter()
. Czy powinno się to wzywać .close()
po tym, OutputStream
jak zostało napisane?
Z jednej strony istnieje zachęta Blocha, aby zawsze zamykać OutputStream
s. Z drugiej strony nie sądzę, że w tym przypadku istnieje podstawowy zasób, który należy zamknąć. Otwieranie / zamykanie gniazd jest zarządzane na poziomie HTTP, aby umożliwić takie rzeczy, jak trwałe połączenia i tym podobne.
java
servlets
outputstream
Steven Huwig
źródło
źródło
close()
, co nic nie robi. Co ty powinien zrobić jest blisko każdego zasobu zamykany.Odpowiedzi:
Zwykle nie należy zamykać strumienia. Kontener serwletów automatycznie zamknie strumień po zakończeniu działania serwletu w ramach cyklu życia żądania serwletu.
Na przykład, jeśli zamkniesz strumień, nie będzie on dostępny, jeśli zaimplementujesz filtr .
Powiedziawszy to wszystko, jeśli go zamkniesz, nic złego się nie stanie, o ile nie spróbujesz go ponownie użyć.
EDYCJA: kolejny link do filtra
EDIT2: adrian.tarau jest poprawny w tym sensie, że jeśli chcesz zmienić odpowiedź po tym, jak aplet wykonał swoje zadanie, powinieneś utworzyć opakowanie rozszerzające HttpServletResponseWrapper i buforować wyjście. Ma to na celu zapobieganie wysyłaniu danych wyjściowych bezpośrednio do klienta, ale także pozwala chronić, jeśli serwlet zamknie strumień, zgodnie z tym fragmentem (moje wyróżnienie):
Artykuł
Z oficjalnego artykułu firmy Sun można wywnioskować, że zamknięcie
OutputStream
serwletu jest czymś normalnym, ale nie jest obowiązkowe.źródło
Ogólna zasada jest taka: jeśli otworzyłeś strumień, powinieneś go zamknąć. Jeśli nie, nie powinieneś. Upewnij się, że kod jest symetryczny.
W przypadku
HttpServletResponse
jest to trochę mniej wyraźne cięcie, ponieważ nie jest oczywiste, czy wywołaniegetOutputStream()
jest operacją, która otwiera strumień. Javadoc po prostu mówi, że to "Returns a ServletOutputStream
"; podobnie dlagetWriter()
. Tak czy inaczej, jasne jest, żeHttpServletResponse
„jest właścicielem” strumienia / modułu zapisującego i to on (lub kontener) jest odpowiedzialny za jego ponowne zamknięcie.A więc odpowiadając na twoje pytanie - nie, w tym przypadku nie powinieneś zamykać strumienia. Kontener musi to zrobić, a jeśli dostaniesz się tam wcześniej, ryzykujesz wprowadzenie subtelnych błędów do aplikacji.
źródło
close()
gdy skończyłem ze strumieniem, klient natychmiast wraca, a reszta serwletu kontynuuje wykonywanie. Czy to nie sprawia, że odpowiedź jest nieco bardziej względna? w przeciwieństwie do ostatecznego tak lub nieJeśli istnieje jakakolwiek szansa, że filtr może zostać wywołany na zasobie „uwzględnionym”, zdecydowanie nie należy zamykać strumienia. Spowoduje to niepowodzenie dołączania zasobu i wyjątek „strumień zamknięty”.
źródło
Powinieneś zamknąć strumień, kod jest czystszy, ponieważ wywołujesz getOutputStream (), a strumień nie jest przekazywany do ciebie jako parametr, kiedy zwykle po prostu go używasz i nie próbujesz go zamknąć. Servlet API nie stwierdza, że jeśli strumień wyjściowy może być zamknięty lub nie może być zamknięty, w tym przypadku możesz bezpiecznie zamknąć strumień, każdy kontener na zewnątrz dba o zamknięcie strumienia, jeśli nie został zamknięty przez serwlet.
Oto metoda close () w Jetty, zamykają strumień, jeśli nie jest zamknięty.
public void close() throws IOException { if (_closed) return; if (!isIncluding() && !_generator.isCommitted()) commitResponse(HttpGenerator.LAST); else flushResponse(); super.close(); }
Również jako twórca filtru nie powinieneś zakładać, że strumień OutputStream nie jest zamknięty. Zawsze powinieneś przekazywać inny strumień wyjściowy, jeśli chcesz zmienić zawartość po wykonaniu przez serwlet swojej pracy.
EDYCJA: Zawsze zamykam strumień i nie miałem żadnych problemów z Tomcat / Jetty. Myślę, że nie powinieneś mieć problemów z jakimkolwiek kontenerem, starym lub nowym.
źródło
Kolejny argument przeciwko zamykaniu
OutputStream
. Spójrz na ten serwlet. Rzuca wyjątek. Wyjątek jest mapowany w pliku web.xml na błąd 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(); } } }
Plik web.xml zawiera:
<?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>
A błąd.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>
Po załadowaniu
/Erroneous
w przeglądarce zobaczysz stronę błędu z komunikatem „Błąd”. Ale jeśli usuniesz komentarz zout.close()
linii w powyższym serwlecie, ponownie wdrożysz aplikację i przeładujesz/Erroneous
, nic nie zobaczysz w przeglądarce. Nie mam pojęcia, co się właściwie dzieje, ale myślę, że toout.close()
uniemożliwia obsługę błędów.Testowane z Tomcat 7.0.50, Java EE 6 przy użyciu Netbeans 7.4.
źródło