在 Java Servlet 中,可以通过response.getOutputStream()
或访问响应正文response.getWriter()
。写入后是否应该调用.close()
它?OutputStream
一方面,有 Blochian 劝告总是关闭OutputStream
s。另一方面,我不认为在这种情况下存在需要关闭的底层资源。套接字的打开/关闭在 HTTP 级别进行管理,以允许诸如持久连接之类的事情。
在 Java Servlet 中,可以通过response.getOutputStream()
或访问响应正文response.getWriter()
。写入后是否应该调用.close()
它?OutputStream
一方面,有 Blochian 劝告总是关闭OutputStream
s。另一方面,我不认为在这种情况下存在需要关闭的底层资源。套接字的打开/关闭在 HTTP 级别进行管理,以允许诸如持久连接之类的事情。
通常你不应该关闭流。作为 servlet 请求生命周期的一部分,servlet 容器将在 servlet 完成运行后自动关闭流。
例如,如果您关闭了流,那么如果您实现了Filter,它将不可用。
说了这么多,如果你关闭它,只要你不尝试再次使用它,就不会发生任何不好的事情。
编辑:另一个过滤器链接
EDIT2:adrian.tarau 是正确的,如果你想在 servlet 完成它的事情后改变响应,你应该创建一个扩展 HttpServletResponseWrapper 的包装器并缓冲输出。这是为了防止输出直接发送到客户端,但也允许您保护 servlet 是否关闭流,根据以下摘录(强调我的):
修改响应的过滤器 通常必须在响应返回给客户端之前捕获它。执行此操作的方法是将生成响应的 servlet 传递给替代流。替代流防止 servlet 在完成时关闭原始响应流,并允许过滤器修改 servlet 的响应。
从那篇 Sun 官方文章中可以推断,关闭OutputStream
servlet 是正常现象,但不是强制性的。
他们的一般规则是:如果你打开了流,那么你应该关闭它。如果你没有,你不应该。确保代码是对称的。
在 的情况下HttpServletResponse
,它有点不太清楚,因为调用getOutputStream()
是否是打开流的操作并不明显。Javadoc 只是说它“ Returns a ServletOutputStream
”;同样对于getWriter()
. 无论哪种方式,清楚的是HttpServletResponse
“拥有”流/编写器,并且它(或容器)负责再次关闭它。
所以回答你的问题 - 不,在这种情况下你不应该关闭流。容器必须这样做,如果您在它之前进入容器,您可能会在应用程序中引入细微的错误。
如果有可能在“包含”资源上调用过滤器,则绝对不应关闭流。这将导致包含资源失败并出现“流关闭”异常。
您应该关闭流,代码更干净,因为您调用 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();
}
此外,作为过滤器的开发人员,您不应假定 OutputStream 未关闭,如果您想在 servlet 完成其工作后更改内容,则应始终传递另一个 OutputStream。
编辑:我总是关闭流,我对 Tomcat/Jetty 没有任何问题。我认为您应该对任何新旧容器都没有任何问题。
反对关闭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()
阻止错误处理。
使用 Tomcat 7.0.50 进行测试,Java EE 6 使用 Netbeans 7.4。
如果您将 Spring 与 Spring Security 一起使用,则不应关闭流或编写器。
返回的 StreamServletResponse.getOutputStream()
或返回的 writerServletResponse.getWriter()
将在关闭时提交响应。提交响应,如此处所述,意味着 http 状态和标头变得不可变,即使在处理此请求期间抛出异常,Spring 框架也无法调整 http 状态。
类的实例OnCommittedResponseWrapper
用作实现,ServletResponse
这里是负责此行为的代码(也请检查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
。
Only after that the exception will start propagating from this controller to Spring error handlers. Spring error handlers will not be able to change the status to 500
because the response was committed already and so the status will remain 200
.