2010-02-16 31 views
5

Tôi đang làm việc và tìm hiểu về JSF + Facelets những ngày này. Tôi có một trang BackingBean và một trang XHTML Facelet. Khi tôi yêu cầu trang facelet (chỉ một lần) phương thức sao lưu bean được gọi nhiều lần.Tại sao phương thức BackingBean được gọi nhiều lần khi yêu cầu facelet?

Điều gì có thể là lý do cho điều này?

Tôi không thể thấy bất kỳ điều gì đặc biệt. Cảm ơn trước.

Đây là facelet:

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets"> 
<head> 
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
<title>Insert title here</title> 
</head> 
<body> 
<ui:composition template="index.xhtml"> 
    <ui:define name="content"> 
     <h:form>Name: <h:inputText id="nameFilterPattern" value="#{kundenBackingBean.nameFilterPattern}" /><h:commandButton value="Suchen"/></h:form> 
     <h:dataTable var="kunde" value="#{kundenBackingBean.kunden}" rowClasses="rowHighlight, rowOrdinary"> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Kundennr" /> 
       </f:facet> 
       <h:outputText value="#{kunde.kundenNr}"/> 
      </h:column> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Name" /> 
       </f:facet> 
       <h:outputText value="#{kunde.name}"/> 
      </h:column> 
      <h:column> 
       <f:facet name="header"> 
        <h:outputText value="Vorname" /> 
       </f:facet> 
       <h:outputText value="#{kunde.vorname}"/> 
      </h:column> 
      <h:column> 
       <h:outputLink>Details</h:outputLink> 
      </h:column> 
     </h:dataTable> 
    </ui:define> 
</ui:composition> 
</body> 
</html> 

Và đây là sự ủng hộ-đậu. Phương pháp getKunden được gọi nhiều lần:

@ManagedBean 
@SessionScoped 
public class KundenBackingBean extends AbstractBackingBean { 

    private String nameFilterPattern; 

    public List<Kunde> getKunden(){ 
     System.out.println("getKunden"); 
     return getApplication().getKunden(getNameFilterPattern()); 
    } 

    public String getNameFilterPattern() { 
     return nameFilterPattern; 
    } 

    public void setNameFilterPattern(String nameFilterPattern) { 
     System.out.println("Name filter: " + nameFilterPattern); 
     this.nameFilterPattern = nameFilterPattern; 
    } 

} 
+0

luôn được gọi cùng một số lần? – volvox

+0

bạn có sử dụng chế độ gỡ lỗi IDE để xác minh rằng các phương thức được gọi nhiều lần hoặc bạn thấy nó trong nhật ký không? – Roman

+0

Nó có vẻ như ở yêu cầu đầu tiên nó được gọi là 8 lần và sau đó nó được gọi là 21 lần.Tôi sử dụng eclipse + glassfish và bắt đầu nó trong chế độ gỡ lỗi. Tôi chỉ cần chèn một sysout vào phương thức và đếm kết quả đầu ra của giao diện điều khiển. – c0d3x

Trả lời

8

Getters của một bean chỉ ở đó để truy cập dữ liệu mô hình từ phía bên xem. Chúng có thể được gọi nhiều lần. Thông thường một hoặc hai lần, nhưng điều này có thể tăng lên đến hàng trăm lần, đặc biệt khi được sử dụng trong các thành phần UIData hoặc trong các thuộc tính khác hơn value (như rendered, disabled, v.v ...). Điều này không bình thường không gây hại, vì nó chỉ là một lời gọi phương thức đơn giản và làm việc tải dữ liệu logic đắt tiền hoặc tính toán thường không được thực hiện trong getters. Nạp trước/khởi tạo thường được thực hiện trong phương thức khởi tạo bean và/hoặc các phương thức hành động bean. Getters nên trên thực tế chỉ có trả lại dữ liệu (nếu cần cũng làm tải chậm).

Nếu getApplication().getKunden(getNameFilterPattern()); đang làm một nhiệm vụ khá đắt tiền, bạn thực sự cần di chuyển nó đến một trong hai constructor đậu hoặc đậu @PostConstruct phương pháp, hoặc khối khởi tạo bean, hoặc phương pháp hành động đậu, hoặc giới thiệu lười tải pattern trong getter. Dưới đây là một ví dụ trong đó cho thấy làm thế nào để làm điều này tất cả:

public class Bean { 
    private String nameFilterPattern; 
    private List<Kunde> kunden; 

    // Load during bean construction. 
    public Bean() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR load during @PostConstruct (will be invoked AFTER construction and resource injection. 
    @PostConstruct 
    public void init() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR during bean initialization (this is invoked BEFORE construction and will apply to ALL constructors). 
    { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
    } 

    // OR during bean action method (invoked from h:commandLink/Button). 
    public String submit() { 
     this.kunden = getApplication().getKunden(getNameFilterPattern()); 
     return "navigationCaseOutcome"; 
    } 

    // OR using lazy loading pattern in getter method. 
    public List<Kunde> getKunden() { 
     if (this.kunden == null) 
      this.kunden = getApplication().getKunden(getNameFilterPattern()); 
     } 
     return this.kunden; 
    } 

Trong trường hợp cụ thể của bạn, tôi nghĩ rằng đó là @PostConstruct (nếu nameFilterPattern là được lấy từ một tham số GET theo yêu cầu), hay chỉ là phương pháp hành động đậu (nếu nameFilterPattern được lấy từ trường nhập POST biểu mẫu) phù hợp.

Để tìm hiểu thêm về vòng đời JSF, bạn có thể tìm thấy điều này hữu ích self-practice article.

+0

Câu trả lời hay, btw :) http://stackoverflow.com/questions/2090033/why-jsf-calls-getters-multiple-times – ewernli

+0

Vâng, đó chắc chắn không phải là lần đầu tiên tôi trả lời như vậy :) Tôi đã xiên nó ít nhất 20 lần trước. Không chỉ ở đây, mà còn ở forums.sun.com và như vậy. – BalusC

2

Nó có thể được gọi từ khác nhau phases của lifecylce JSF. Đặt cược của tôi sẽ là các giai đoạn RestoreView và sau đó RenderResponse - Gần đây tôi chưa sử dụng JSF, vì vậy tôi không nhớ chi tiết này.

Bạn có thể lưu bộ nhớ cache mẫu bộ lọc mới nhất và các ứng dụng khách tương ứng. Bạn chỉ tải lại máy khách nếu bộ lọc thay đổi. Bằng cách này, bạn giải quyết vấn đề cụ thể này, cộng với tránh tải lại dữ liệu nếu bộ lọc không thay đổi.

private String nameFilterPattern; 
private String lastNameFilterPatternLoaded; 
private List<Kunde> clients; 

public List<Kunde> getKunden(){ 
    System.out.println("getKunden"); 
    if(nameFilterPattern.equals(lastNameFilterPatternLoaded)) 
    { 
     clients = getApplication().getKunden(getNameFilterPattern()); 
     lastNameFilterPatternLoaded = nameFilterPattern 
    } 
    return clients; 
} 

Hoặc bạn có thể sử dụng một bean request (thay vì session) và chắc chắn rằng bạn tải dữ liệu một lần duy nhất cho mỗi yêu cầu.

+0

Cảm ơn bạn đã trả lời. Tôi đã thay đổi đậu để yêu cầu phạm vi. Hành vi là như nhau. Làm cách nào để đảm bảo tải dữ liệu chỉ trên mỗi yêu cầu? Như bạn có thể thấy trong facelet, phương thức này chỉ được tham chiếu một lần. Điều này có thể được liên quan đến các công cụ mẫu: ui: composition/ui: define? – c0d3x

+0

Câu trả lời từ BalusC mà bạn đã chấp nhận bao gồm tất cả. Tôi đoán bạn không cần giải thích thêm. – ewernli

Các vấn đề liên quan