我用谷歌搜索找不到权威的答案。在Java servlet中,可以通过response.getOutputStream()或response.getWriter()访问响应主体。是否应该在写入?
之后在此流上调用.close()一方面,Blochian劝告总是关闭输出流。另一方面,我认为在这种情况下不存在需要关闭的底层资源。套接字的打开/关闭在HTTP级别进行管理,以允许持久连接之类的事情。
答案 0 :(得分:84)
通常你不应该关闭流。作为servlet请求生命周期的一部分,servlet完成运行后,servlet容器将自动关闭流。
例如,如果您关闭了流,那么如果您实施了Filter,它将无法使用。
说了这么多,如果你关闭它,只要你不再尝试使用它就不会发生任何不好的事。
EDIT2:adrian.tarau是正确的,因为如果你想在servlet完成它之后改变响应,你应该创建一个包装器来扩展HttpServletResponseWrapper并缓冲输出。这是为了防止输出直接进入客户端,但是如果servlet关闭了流,也可以保护输出,如本节摘录(强调我的):
修改响应的过滤器必须 通常捕获之前的响应 返回给客户。通往的方式 这样做是为了传递那个servlet 生成响应的替身 流。替身流阻止 关闭原始的servlet 响应流完成时和 允许过滤器修改 servlet的回应。
可以从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
。