是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?


96

在Java Servlet中,可以通过response.getOutputStream()或访问响应主体response.getWriter()。如果一个呼叫.close()在这OutputStream之后,它已经被写入?

一方面,总有一个Blochian劝告要始终关闭OutputStreams。另一方面,我不认为在这种情况下有需要关闭的基础资源。套接字的打开/关闭在HTTP级别进行管理,以允许诸如持久连接之类的事情。


不邀请您猜测是否有基础资源要关闭。如果实现者这样认为,或者更确切地知道,那么他将提供close()什么都不做的。什么,你应该做的是接近每一个可关闭的资源。
罗恩侯爵,

1
即使您的代码没有打开?我不这么认为……
史蒂芬·胡维格

Answers:


93

通常,您不应该关闭流。在servlet完成作为servlet请求生命周期的一部分运行之后,servlet容器将自动关闭流。

例如,如果您关闭流,那么在实现Filter的情况下将不可用。

说了这么多,只要您关闭它,只要您不尝试再次使用它,都不会发生任何不良情况。

编辑:另一个筛选器链接

EDIT2:adrian.tarau是正确的,因为如果您想在Servlet完成其操作后更改响应,则应创建一个扩展HttpServletResponseWrapper的包装器并缓冲输出。这是为了防止输出直接进入客户端,但也允许您保护servlet是否关闭流,如以下摘录(强调我的话):

修改响应的过滤器通常必须 在将响应返回给客户端之前捕获该响应。实现此目的的方法是将生成响应的servlet传递给替代流。替身流阻止servlet在完成时关闭原始响应流,并允许过滤器修改servlet的响应。

文章

从Sun的官方文章可以推断出 OutputStream从servlet属于正常现象,但不是强制性的。


2
这是对的。需要注意的一件事是,在某些情况下,您可能需要冲洗流,这是完全允许的。
toluju

1
关闭编写器还有另一个副作用。关闭状态码后,您也将无法通过response.setStatus设置状态码。
che javara

1
请遵循此建议。这将减轻您的痛苦。除非您知道为什么要这样做,否则我也不会flush()-应该让容器处理缓冲。
Hal50000 2014年

75

它们的一般规则是:如果打开流,则应将其关闭。如果没有,就不应该。确保代码是对称的。

在的情况下HttpServletResponse,它没有那么明确,因为调用getOutputStream()是否是打开流的操作并不明显。Javadoc只是说“ Returns a ServletOutputStream”;类似的getWriter()。无论哪种方式,都清楚的是HttpServletResponse流/写入器“拥有”,并且它(或容器)负责再次关闭它。

因此,请回答您的问题-不,在这种情况下,您不应该关闭流。容器必须做到这一点,如果您在此之前就进入了该容器,则可能会在应用程序中引入细微的错误。


我同意这个答案,您可能还想签出ServletResponse.flushBuffer(),请参阅:docs.oracle.com/javaee/1.4/api/javax/servlet/…– Cyber
-monk

14
“如果打开了流,那么应该关闭它。如果没有,就不应该” —-说得很好
Srikanth Reddy Lingala

2
感觉就像在学校董事会上张贴的一句话:“如果打开它,请关闭它。如果打开它,请关闭它。如果您将它解锁,请锁定它。[...]”
里卡多2015年

iv注意到我是在代码的早期使用流,再也没有,客户端等待整个servlet执行,但是当我close()在流完成后调用时,客户端立即返回,其余servlet继续执行。那会不会使答案更具相对性?而不是确定的是或否
BiGGZ

“如果打开了流,则应将其关闭。否则,请不要。确保代码对称。” -那么如果您随后创建另一个包装此流的流,该怎么办呢?在这种情况下,很难保持对称,因为调用外部流通常会关闭嵌套流。
steinybot

5

如果有可能在“包含”资源上调用过滤器,则绝对不要关闭流。这将导致包含资源因“流关闭”异常而失败。


感谢您添加此评论。非常有趣的是,我仍然在一点点解决这个问题–刚才我注意到NetBeans的servlet模板确实包含关闭输出流的代码...
Steven Huwig 2010年

4

您应该关闭流,因为调用getOutputStream()会使代码更简洁,并且通常不使用流就将其作为参数传递给您,而通常只使用它而不尝试关闭它。Servlet API并未声明如果可以关闭输出流或必须关闭输出流,在这种情况下,您可以安全地关闭流,如果该流没有被Servlet关闭,则其中的任何容器都将关闭该流。

这是Jetty中的close()方法,如果未关闭流,它们将关闭流。

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

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

        super.close();
    }

此外,作为Filter的开发人员,您不应假定未关闭OutputStream,如果要在Servlet完成其工作后更改内容,则应始终传递另一个OutputStream。

编辑:我总是关闭流,并且我对Tomcat / Jetty没有任何问题。我认为您对任何新旧容器都不应该有任何问题。


4
“您应该关闭流,代码更干净……”-对我来说,使用.close()进行编码的代码看起来要比不使用.close()的代码更不干净,尤其是如果不需要.close()的时候,这是这个问题。试图确定。
史蒂芬·休伊格

1
是的,但是很漂亮:)无论如何,由于API尚不清楚,所以我希望关闭它,代码看起来是一致的,一旦您请求OutputStream,就应该关闭它,除非API说“不关闭它”。
adrian.tarau

我认为在我自己的代码中关闭输出流不是一个好习惯。那就是容器的工作。
JimHawkins '16

get *不会创建流​​,它不是生产者。您不应该关闭它,容器必须这样做。
dmatej '16

3

另一种反对关闭市场的论点OutputStream。看一下这个servlet。引发异常。异常在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()上述servlet中的行,重新部署应用程序,然后重新加载,/Erroneous您将在浏览器中看不到任何内容。我不知道实际发生了什么,但我想那是out.close()可以防止错误处理。

使用Netbeans 7.4在Tomcat 7.0.50和Java EE 6上进行了测试。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.