Một người có nên gọi .close () trên HttpServletResponse.getOutputStream () /. GetWriter ()?


96

Trong Java Servlet, người ta có thể truy cập phần thân phản hồi thông qua response.getOutputStream()hoặc response.getWriter(). Một người có nên gọi .close()về điều này OutputStreamsau khi nó đã được viết cho?

Một mặt, có lời khuyến khích Blochian luôn đóng cửa OutputStreams. Mặt khác, tôi không nghĩ rằng trong trường hợp này có một tài nguyên cơ bản cần phải đóng lại. Việc mở / đóng các ổ cắm được quản lý ở cấp HTTP, để cho phép những thứ như kết nối liên tục và tương tự.


Bạn không được mời đoán xem có tài nguyên cơ bản nào sẽ bị đóng hay không. Nếu người thực hiện nghĩ như vậy, hoặc đúng hơn là biết như vậy, anh ta sẽ cung cấp một close()điều không làm gì cả. Những gì bạn nên làm là đóng mọi tài nguyên có thể đóng được.
Marquis of Lorne

1
Ngay cả khi mã của bạn không mở nó? Tôi không nghĩ vậy ...
Steven Huwig

Câu trả lời:


93

Thông thường bạn không nên đóng luồng. Vùng chứa servlet sẽ tự động đóng luồng sau khi servlet chạy xong như một phần của vòng đời yêu cầu servlet.

Ví dụ: nếu bạn đóng luồng, nó sẽ không khả dụng nếu bạn triển khai Bộ lọc .

Đã nói tất cả những điều đó, nếu bạn đóng nó sẽ không có gì xấu xảy ra miễn là bạn không cố sử dụng lại.

CHỈNH SỬA: một liên kết bộ lọc khác

EDIT2: adrian.tarau đúng ở chỗ nếu bạn muốn thay đổi phản hồi sau khi servlet đã hoàn thành công việc của nó, bạn nên tạo một trình bao bọc mở rộng HttpServletResponseWrapper và đệm đầu ra. Điều này là để giữ đầu ra không đi trực tiếp đến máy khách nhưng cũng cho phép bạn bảo vệ nếu servlet đóng luồng, như theo đoạn trích này (tôi nhấn mạnh):

Bộ lọc sửa đổi phản hồi thường phải nắm bắt phản hồi trước khi nó được trả lại cho máy khách. Cách thực hiện là chuyển servlet tạo ra phản hồi một luồng dự phòng. Luồng dự phòng ngăn không cho servlet đóng luồng phản hồi ban đầu khi nó hoàn thành và cho phép bộ lọc sửa đổi phản hồi của servlet.

Bài báo

Người ta có thể suy ra từ bài báo chính thức của Sun rằng việc đóng OutputStreamtừ một servlet là một điều gì đó xảy ra bình thường, nhưng không phải là bắt buộc.


2
Chính xác. Một điều cần lưu ý là trong một số trường hợp, bạn có thể phải xả luồng và điều đó hoàn toàn được phép.
toluju

1
Có một tác dụng phụ khác của việc đóng cửa nhà văn. Bạn cũng sẽ không thể đặt mã trạng thái thông qua response.setStatus sau khi nó đã bị đóng.
che javara

1
Làm theo lời khuyên này. Nó sẽ giúp bạn đỡ đau đớn. Tôi cũng sẽ không tuôn ra () trừ khi bạn biết tại sao bạn lại làm điều đó - bạn nên để vùng chứa xử lý bộ đệm.
Hal50000

75

Quy tắc chung của họ là: nếu bạn đã mở luồng, thì bạn nên đóng nó. Nếu bạn đã không, bạn không nên. Đảm bảo mã là đối xứng.

Trong trường hợp của HttpServletResponse, nó ít rõ ràng hơn một chút, vì không rõ ràng việc gọi getOutputStream()có phải là một thao tác mở luồng hay không. Javadoc chỉ nói rằng nó " Returns a ServletOutputStream"; tương tự cho getWriter(). Dù bằng cách nào, điều rõ ràng là HttpServletResponse"sở hữu" luồng / người viết và nó (hoặc vùng chứa) chịu trách nhiệm đóng lại luồng đó.

Vì vậy, để trả lời câu hỏi của bạn - không, bạn không nên đóng luồng trong trường hợp này. Vùng chứa phải làm điều đó và nếu bạn vào đó trước nó, bạn có nguy cơ giới thiệu các lỗi nhỏ trong ứng dụng của mình.


Tôi đồng ý với câu trả lời này, bạn cũng có thể muốn kiểm tra các ServletResponse.flushBuffer () xem: docs.oracle.com/javaee/1.4/api/javax/servlet/...
cyber-sư

14
"nếu bạn đã mở luồng, thì bạn nên đóng nó. Nếu bạn không mở, bạn không nên" --- đã nói tốt
Srikanth Reddy Lingala

2
Nó cảm thấy như câu được đăng trên hội đồng nhà trường "nếu bạn mở nó, gần nó Nếu bạn bật tính năng này, tắt nó đi Nếu bạn mở khóa, khóa nó lên [...]..."
Ricardo

Iv nhận thấy rằng tôi sử dụng luồng sớm trong mã của mình và không bao giờ lặp lại nữa, máy khách đợi toàn bộ servlet thực thi, nhưng khi tôi gọi close()khi tôi thực hiện xong luồng, máy khách trả về ngay lập tức và phần còn lại của servlet tiếp tục thực thi. Điều đó không làm cho câu trả lời tương đối hơn một chút? như trái ngược với một vâng dứt khoát hay không
BiGGZ

"nếu bạn đã mở luồng, thì bạn nên đóng nó. Nếu không, bạn không nên. Hãy đảm bảo rằng mã là đối xứng." - Vậy nếu sau đó bạn tạo một luồng khác kết thúc luồng này thì sao? Khó duy trì tính đối xứng trong trường hợp này vì việc gọi đóng luồng bên ngoài thường sẽ đóng luồng lồng nhau.
steinybot

5

Nếu có bất kỳ khả năng nào bộ lọc có thể được gọi trên tài nguyên 'được bao gồm', bạn chắc chắn không nên đóng luồng. Điều này sẽ khiến tài nguyên bao gồm không thành công với ngoại lệ "luồng đã đóng".


Cảm ơn đã thêm bình luận này. Hilariously đủ tôi vẫn đang phải vật lộn với vấn đề này một chút - chỉ là bây giờ tôi nhận thấy mẫu servlet NetBeans của không bao gồm mã để đóng dòng đầu ra ...
Steven Huwig

4

Bạn nên đóng luồng, mã sạch hơn vì bạn gọi getOutputStream () và luồng không được chuyển cho bạn dưới dạng tham số, thông thường bạn chỉ sử dụng nó và không cố đóng nó. API Servlet không nói rằng nếu luồng đầu ra có thể được đóng hoặc không được đóng, trong trường hợp này bạn có thể đóng luồng một cách an toàn, bất kỳ vùng chứa nào ngoài đó sẽ đóng luồng nếu nó không được đóng bởi servlet.

Đây là phương thức close () trong Jetty, chúng đóng luồng nếu nó chưa đóng.

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

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

        super.close();
    }

Ngoài ra, với tư cách là nhà phát triển Bộ lọc, bạn không nên cho rằng Dòng đầu ra chưa bị đóng, bạn luôn phải chuyển Dòng đầu ra khác nếu bạn muốn thay đổi nội dung sau khi servlet đã hoàn thành công việc của nó.

CHỈNH SỬA: Tôi luôn đóng luồng và tôi không gặp bất kỳ sự cố nào với Tomcat / Jetty. Tôi không nghĩ rằng bạn nên gặp bất kỳ vấn đề gì với bất kỳ thùng chứa nào, cũ hay mới.


4
"Bạn nên đóng luồng, mã sạch hơn ..." - Đối với tôi, mã có .close () trông kém sạch hơn mã không có, đặc biệt nếu .close () là không cần thiết - đó là câu hỏi này cố gắng xác định.
Steven Huwig

1
yep, nhưng vẻ đẹp đến sau :) Dù sao, vì API không rõ ràng nên tôi muốn đóng nó lại, mã có vẻ nhất quán, một khi bạn yêu cầu Dòng đầu ra, bạn nên đóng nó trừ khi API nói "không đóng nó".
adrian.tarau

Tôi không nghĩ rằng đóng luồng đầu ra trong mã của riêng mình là một cách thực hành tốt. Đó là công việc của container.
JimHawkins

get * không tạo luồng, nó không phải là nhà sản xuất. Bạn không nên đóng nó, container phải làm điều đó.
dmatej

3

Một lập luận khác chống lại việc đóng OutputStream. Nhìn vào servlet này. Nó ném ra một ngoại lệ. Ngoại lệ được ánh xạ trong web.xml thành lỗi 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();
    }
  }
}

Tệp web.xml chứa:

<?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>

Và lỗi .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>

Khi bạn tải /Erroneoustrong trình duyệt, bạn thấy trang lỗi hiển thị "Lỗi". Nhưng nếu bạn bỏ nhận xét out.close()dòng trong servlet ở trên, triển khai lại ứng dụng và tải lại, /Erroneousbạn sẽ không thấy gì trong trình duyệt. Tôi không có manh mối nào về những gì đang thực sự xảy ra, nhưng tôi đoán điều đó out.close()ngăn cản việc xử lý lỗi.

Đã thử nghiệm với Tomcat 7.0.50, Java EE 6 bằng Netbeans 7.4.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.