Java 应该在 HttpServletResponse.getOutputStream()/.getWriter() 上调用 .close() 吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1159168/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:22:20  来源:igfitidea点击:

Should one call .close() on HttpServletResponse.getOutputStream()/.getWriter()?

javaservletsoutputstream

提问by Steven Huwig

In Java Servlets, one can access the response body via response.getOutputStream()or response.getWriter(). Should one call .close()on this OutputStreamafter it has been written to?

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

On the one hand, there is the Blochian exhortation to always close OutputStreams. On the other hand, I don't think that in this case there is an underlying resource that needs to be closed. The opening/closing of sockets is managed at the HTTP level, to allow things like persistent connections and such.

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

采纳答案by Nemi

Normally you should not close the stream. The servlet container will automatically close the stream after the servlet is finished running as part of the servlet request life-cycle.

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

For instance, if you closed the stream it would not be available if you implemented a Filter.

例如,如果您关闭了流,如果您实现了Filter,它将不可用。

Having said all that, if you do close it nothing bad will happen as long as you don't try to use it again.

话虽如此,如果您关闭它,只要您不尝试再次使用它,就不会发生任何坏事。

EDIT: another filter link

编辑:另一个过滤器链接

EDIT2: adrian.tarau is correct in that if you want to alter the response after the servlet has done its thing you should create a wrapper extending HttpServletResponseWrapper and buffer the output. This is to keep the output from going directly to the client but also allows you to protect if the servlet closes the stream, as per this excerpt (emphasis mine):

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

A filter that modifies a response must usuallycapture the response before it is returned to the client. The way to do this is to pass the servlet that generates the response a stand-in stream. The stand-in stream prevents the servlet from closing the original response stream when it completes and allows the filter to modify the servlet's response.

修改响应的过滤器通常必须 在响应返回给客户端之前捕获响应。这样做的方法是将生成响应的 servlet 传递给一个替代流。替代流可防止 servlet 在完成时关闭原始响应流,并允许过滤器修改 servlet 的响应。

Article

文章

One can infer from that official Sun article that closing the OutputStreamfrom a servlet is something that is a normal occurrence, but is not mandatory.

人们可以从 Sun 的那篇官方文章中推断出,关闭OutputStreamservlet 是一种正常现象,但不是强制性的。

回答by adrian.tarau

You should close the stream, the code is cleaner since you invoke getOutputStream() and the stream is not passed to you as a parameter, when usually you just use it and don't attempt to close it. The Servlet API doesn't states that if the output stream can be closed or must not be closed, in this case you can safely close the stream, any container out there takes care of closing the stream if it was not closed by the servlet.

您应该关闭流,代码更清晰,因为您调用了 getOutputStream() 并且流不会作为参数传递给您,通常您只是使用它而不尝试关闭它。Servlet API 没有说明是否可以关闭或不得关闭输出流,在这种情况下,您可以安全地关闭流,如果流没有被 servlet 关闭,则那里的任何容器都会负责关闭流。

Here is the close() method in Jetty, they close the stream if it not closed.

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

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

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

        super.close();
    }

Also as a developer of a Filter you should not presume that the OutputStream is not closed, you should always pass another OutputStream if you want to alter the content after the servlet has done its job.

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

EDIT : I'm always closing the stream and I didn't had any problems with Tomcat/Jetty. I don't think you should have any problems with any container, old or new.

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

回答by skaffman

The general rule of them is this: if you opened the stream, then you should close it. If you didn't, you shouldn't. Make sure the code is symmetric.

它们的一般规则是:如果您打开了流,那么您应该关闭它。如果你没有,你不应该。确保代码是对称的。

In the case of HttpServletResponse, it's a bit less clear cut, since it's not obvious if calling getOutputStream()is an operation that opens the stream. The Javadoc just says that it "Returns a ServletOutputStream"; similarly for getWriter(). Either way, what is clear is that HttpServletResponse"owns" the stream/writer, and it (or the container) is responsible for closing it again.

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

So to answer your question - no, you should not close the stream in this case. The container must do that, and if you get in there before it, you risk introducing subtle bugs in your application.

所以要回答你的问题 - 不,在这种情况下你不应该关闭流。容器必须这样做,如果你在它之前进入,你就有可能在你的应用程序中引入细微的错误。

回答by Skip Head

If there is any chance the filter might be called on an 'included' resource, you should definitely notclose the stream. This will cause the including resource to fail with a 'stream closed' exception.

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

回答by user1872904

Another argument against closing the OutputStream. Look at this servlet. It throws an exception. The exception is mapped in the web.xml to an error JSP:

另一个反对关闭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();
    }
  }
}

The web.xml file contains:

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>

And the error.jsp:

和错误.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>

When you load /Erroneousin the browser you see the error page displaying "An error". But if you un-comment the out.close()line in the above servlet, redeploy de application, and reload /Erroneousyou'll see nothing in the browser. I have no clue about what is actually happening, but I guess that out.close()prevents the error handling.

当您/Erroneous在浏览器中加载时,您会看到显示“错误”的错误页面。但是,如果您取消注释out.close()上述 servlet 中的行,重新部署应用程序并重新加载,/Erroneous您将在浏览器中看不到任何内容。我不知道实际发生了什么,但我想这会out.close()阻止错误处理。

Tested with Tomcat 7.0.50, Java EE 6 using Netbeans 7.4.

使用 Tomcat 7.0.50 测试,Java EE 6 使用 Netbeans 7.4。