HttpServletResponse.getOutputStream () /. getWriter ()에서 .close ()를 호출해야합니까?


96

자바 서블릿에서 하나를 통해 응답 본문에 액세스 할 수 있습니다 response.getOutputStream()또는 response.getWriter(). 작성된 후 .close()이것에 대해 OutputStream한 번 전화해야합니까 ?

한편으로는 항상 OutputStreams를 닫으라는 Blochian 권고가 있습니다. 반면에이 경우에는 닫아야하는 기본 리소스가 있다고 생각하지 않습니다. 소켓의 열기 / 닫기는 HTTP 수준에서 관리되어 지속적인 연결 등을 허용합니다.


닫을 기본 리소스가 있는지 추측하도록 초대받지 않았습니다. 구현자가 그렇게 생각하거나 알고 있다면 close()아무것도하지 않는를 제공 할 것입니다. 무엇 당신이 해야 할 것은 닫을 때마다 폐쇄 가능한 자원이다.
Marquis of Lorne

1
코드가 열리지 않더라도? 나는 그렇게 생각하지 않는다 ...
Steven

답변:


93

일반적으로 스트림을 닫으면 안됩니다. 서블릿 컨테이너는 서블릿 요청 수명주기의 일부로 서블릿 실행이 완료된 후 자동으로 스트림을 닫습니다.

예를 들어 스트림을 닫은 경우 Filter 를 구현하면 사용할 수 없습니다 .

모든 것을 말했듯이, 닫으면 다시 사용하지 않는 한 나쁜 일이 발생하지 않습니다.

편집 : 다른 필터 링크

EDIT2 : adrian.tarau는 서블릿이 작업을 수행 한 후 응답을 변경하려면 HttpServletResponseWrapper를 확장하는 래퍼를 만들고 출력을 버퍼링해야한다는 점에서 정확합니다. 이것은 출력이 클라이언트로 직접 전달되는 것을 막기위한 것이지만이 발췌문 (강조 내)에 따라 서블릿이 스트림을 닫는 경우 보호 할 수도 있습니다.

응답을 수정하는 필터는 일반적으로 클라이언트로 반환되기 전에 응답을 캡처 해야합니다 . 이를 수행하는 방법은 응답을 생성하는 서블릿을 대기 스트림으로 전달하는 것입니다. 대기 스트림은 서블릿이 완료 될 때 원래 응답 스트림을 닫는 것을 방지하고 필터가 서블릿의 응답을 수정할 수 있도록합니다.

공식 Sun 기사 OutputStream에서 서블릿에서 를 닫는 것은 정상적인 현상이지만 필수는 아니라는 것을 추론 할 수 있습니다 .


2
맞습니다. 한 가지주의 할 점은 경우에 따라 스트림을 플러시해야 할 수도 있으며 이는 완벽하게 허용된다는 것입니다.
toluju

1
작가를 닫는 또 다른 부작용이 있습니다. 또한 닫힌 후에는 response.setStatus를 통해 상태 코드를 설정할 수 없습니다.
che javara 2011

1
이 조언을 따르십시오. 그것은 당신에게 많은 고통을 덜어 줄 것입니다. 왜 그렇게하는지 알지 못한다면 나는 flush ()하지 않을 것이다. 컨테이너가 버퍼링을 처리하도록해야한다.
Hal50000 2014 년

75

일반적인 규칙은 다음과 같습니다. 스트림을 열었다면 닫아야합니다. 하지 않았다면하지 말아야합니다. 코드가 대칭인지 확인하십시오.

의 경우 HttpServletResponse호출이 경우는 명확하지이기 때문에, 그것은 조금 덜 명확한의 getOutputStream()스트림을 여는 작업입니다. Javadoc은 " Returns a ServletOutputStream" 라고 말합니다 . 유사하게 getWriter(). 어느 쪽이든 분명한 것은 HttpServletResponse스트림 / 라이터 를 "소유"하고 해당 스트림 / 라이터를 다시 닫을 책임이 있다는 것입니다.

따라서 귀하의 질문에 대답하려면-아니요,이 경우 스트림을 닫으면 안됩니다. 컨테이너는이를 수행해야하며 그 전에 거기에 들어가면 애플리케이션에 미묘한 버그가 발생할 위험이 있습니다.


나는이 답변에 동의, 당신은 또한)합니다 (ServletResponse.flushBuffer 체크 아웃을 참조 할 수 있습니다 : docs.oracle.com/javaee/1.4/api/javax/servlet/...
사이버 승려

14
"스트림을 열었다면 닫아야합니다. 그렇지 않으면하지 말아야합니다."--- 잘 말했습니다
Srikanth Reddy Lingala

2
"열면 닫으세요. 켜면 끄세요. 잠금을 풀면 잠그세요. [...]"
Ricardo

Iv는 코드 초기에 스트림을 사용하고 결코 다시는 사용하지 않는다는 것을 알았습니다. 클라이언트는 전체 서블릿이 실행될 때까지 기다리지 만 close()스트림 작업이 완료되면 호출 하면 클라이언트가 즉시 반환되고 나머지 서블릿은 계속 실행됩니다. 그렇다면 대답을 좀 더 상대적으로 만들지 않습니까? 확실한 예 또는 아니오와 반대
BiGGZ

"스트림을 열었다면 닫아야합니다. 열지 않았 으면하지 말아야합니다. 코드가 대칭인지 확인하십시오." -그렇다면이 스트림을 감싸는 다른 스트림을 만들면 어떨까요? 이 경우 외부 스트림을 닫으면 일반적으로 중첩 된 스트림이 닫히므로 대칭을 유지하기가 어렵습니다.
steinybot

5

필터는 '포함'자원 호출 할 수있는 기회, 당신이해야 확실히있는 경우 하지 가까운 스트림. 이로 인해 포함 리소스가 '스트림 종료'예외와 함께 실패합니다.


이 댓글을 추가해 주셔서 감사합니다. 웃기게도 저는 여전히 문제를 해결하고 있습니다. 방금 NetBeans의 서블릿 템플릿에 출력 스트림을 닫는 코드가 포함되어 있음을 알게되었습니다 ...
Steven Huwig

4

스트림을 닫아야합니다. getOutputStream ()을 호출하고 스트림이 매개 변수로 전달되지 않기 때문에 코드가 더 깔끔합니다. 일반적으로 스트림을 사용하고 닫으려고하지 않을 때입니다. Servlet API는 출력 스트림을 닫을 수 있거나 닫지 말아야한다고 명시하지 않습니다.이 경우 스트림을 안전하게 닫을 수 있으며, 서블릿에 의해 닫히지 않은 경우 외부 컨테이너가 스트림을 닫는 작업을 처리합니다.

Jetty의 close () 메서드는 스트림이 닫히지 않으면 닫습니다.

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

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

        super.close();
    }

또한 필터 개발자로서 OutputStream이 닫혀 있지 않다고 가정해서는 안되며, 서블릿이 작업을 완료 한 후 콘텐츠를 변경하려면 항상 다른 OutputStream을 전달해야합니다.

편집 : 나는 항상 스트림을 닫고 있으며 Tomcat / Jetty에 문제가 없었습니다. 나는 당신이 오래되거나 새 컨테이너에 어떤 문제가 있어야한다고 생각하지 않습니다.


4
"스트림을 닫아야합니다. 코드가 더 깔끔해졌습니다 ..."-나에게 .close ()로 뒤덮인 코드는없는 코드보다 덜 깔끔해 보입니다. 특히 .close ()가 불필요한 경우-이것이 바로이 질문입니다. 결정하려고합니다.
Steven Huwig

1
네,하지만 아름다움은 뒤에옵니다. :) 어쨌든, API가 명확하지 않기 때문에 닫는 것을 선호하기 때문에 코드는 일관된 것처럼 보입니다. 일단 OutputStream을 요청하면 API가 "닫지 마십시오"라고 말하지 않는 한 닫아야합니다.
adrian.tarau

내 코드에서 출력 스트림을 닫는 것이 좋은 방법이라고 생각하지 않습니다. 그것이 컨테이너의 역할입니다.
JimHawkins

get *은 스트림을 생성하지 않으며 생산자가 아닙니다. 당신은 그것을 닫아서는 안되며, 컨테이너는 그것을해야합니다.
dmatej

3

을 닫는에 대한 또 다른 인수 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()오류 처리 를 방해 한다고 생각합니다 .

Netbeans 7.4를 사용하여 Tomcat 7.0.50, Java EE 6에서 테스트되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.