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

时间:2009-07-21 13:27:54

标签: java servlets outputstream

我用谷歌搜索找不到权威的答案。在Java servlet中,可以通过response.getOutputStream()或response.getWriter()访问响应主体。是否应该在写入?

之后在此流上调用.close()

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

6 个答案:

答案 0 :(得分:84)

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

例如,如果您关闭了流,那么如果您实施了Filter,它将无法使用。

说了这么多,如果你关闭它,只要你不再尝试使用它就不会发生任何不好的事。

编辑:another filter link

EDIT2:adrian.tarau是正确的,因为如果你想在servlet完成它之后改变响应,你应该创建一个包装器来扩展HttpServletResponseWrapper并缓冲输出。这是为了防止输出直接进入客户端,但是如果servlet关闭了流,也可以保护输出,如本节摘录(强调我的):

  

修改响应的过滤器必须   通常捕获之前的响应   返回给客户。通往的方式   这样做是为了传递那个servlet   生成响应的替身   流。替身流阻止   关闭原始的servlet   响应流完成时和   允许过滤器修改   servlet的回应。

Article

可以从Sun官方文章中推断出,从servlet关闭输出流是正常现象,但不是强制性的。

答案 1 :(得分:67)

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

HttpServletResponse的情况下,它不太明确,因为调用getOutputStream()是一个打开流的操作并不明显。 Javadoc只是说“Returns a ServletOutputStream”;同样适用于getWriter()。无论哪种方式,很清楚的是HttpServletResponse“拥有”流/作者,它(或容器)负责再次关闭它。

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

答案 2 :(得分:4)

如果有可能在“包含”资源上调用过滤器,您肯定关闭该流。这将导致包含资源因“流关闭”异常而失败。

答案 3 :(得分:3)

您应该关闭流,代码更干净,因为您调用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 :(得分: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时,您会看到显示“错误”的错误页面。 但是,如果您取消注释上述servlet中的out.close()行,重新部署de application,并重新加载/Erroneous,您将在浏览器中看不到任何内容。我不知道实际发生了什么,但我想out.close()可以防止错误处理。

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

答案 5 :(得分:0)

如果您将Spring与Spring Security结合使用,则不应关闭流或编写器。

ServletResponse.getOutputStream()返回的流或从ServletResponse.getWriter()返回的编写器将在关闭时提交响应。以explained here的形式提交响应意味着http状态和标头变得不可变,即使在处理此请求期间抛出异常,Spring框架也无法调整http状态。

OnCommittedResponseWrapper类的实例用作ServletResponse的实现,而这是code负责此行为的对象(也请选中javadoc)。

考虑以下示例控制器:

@RestController
public class MyController {
    @RequestMapping(method = RequestMethod.POST, value = "/blah")
    public void entrypoint(ServletRequest request, ServletResponse response) throws IOException {
        try (var writer = response.getWriter()) {
            throw new RuntimeException("Something bad happened here");
        }
    }

引发异常时,发生的第一件事是对writer.close()的调用,该调用会将响应http状态冻结为默认值200

只有在此之后,异常才会开始从此控制器传播到Spring错误处理程序。 Spring错误处理程序将无法将状态更改为500,因为响应已经提交,因此状态将保持200