2010-09-23 28 views
8

Tôi không chắc chắn nếu những gì tôi đang làm là sai, hoặc nếu tôi chỉ bỏ lỡ một chú thích hoặc mục cấu hình ở đâu đó. Đây là tình huống:JSF - Bean được quản lý theo phiên không có phụ thuộc được tiêm lại trên phiên deserialization

Tôi có một ứng dụng JSF với một bean phiên có phạm vi tên là SessionData. Bean này có một tham chiếu bean có phạm vi ứng dụng (loại ApplicationData) được chèn vào nó vào lúc tạo. Điều này hoạt động tốt khi phiên được tạo lần đầu tiên. Các dependency injection được thực hiện với <managed-bean> yếu tố trong file faces-config.xml như ở đây:

<managed-bean> 
    <managed-bean-name>sessionData</managed-bean-name> 
    <managed-bean-class>my.package.SessionData</managed-bean-class> 
    <managed-bean-scope>session</managed-bean-scope> 
    <managed-property> 
     <property-name>applicationData</property-name> 
     <property-class>my.package.ApplicationData</property-class> 
     <value>#{applicationData}</value> 
    </managed-property> 
</managed-bean> 
<managed-bean> 
    <managed-bean-name>applicationData</managed-bean-name> 
    <managed-bean-class>my.package.ApplicationData</managed-bean-class> 
    <managed-bean-scope>application</managed-bean-scope> 
</managed-bean> 

Bởi vì nó không có ý nghĩa để có đối tượng SessionData tôi bao gồm các đối tượng ApplicationData khi nó đăng, tôi đã đánh dấu sự ApplicationData tài liệu tham khảo như thoáng qua trong đối tượng SessionData tôi:

transient private ApplicationData applicationData; 

Tất cả là tốt cho đến khi ứng dụng web được dừng lại (trong container 6.x Tomcat của tôi) và các buổi là serialized. Khi tôi khởi động lại ứng dụng và các phiên được deserialized, tham chiếu của tôi để ApplicationData không được tiêm lại bởi JSF. Tôi biết rằng deserialization là vụ phải rời khỏi các lĩnh vực thoáng qua mà không có một giá trị. Có cách nào để báo hiệu JSF rằng đối tượng có phạm vi phiên này yêu cầu các phụ thuộc của nó được đặt lại sau khi deserialization không?

Tôi đang sử dụng MyFaces JSF 1.2 và Tomcat 6.0.26 làm vùng chứa ứng dụng web của mình.

+1

Tôi đã gợi ý rằng tôi cung cấp phương thức readObject() và đặt thủ công đối tượng ApplicationData trong đó trong quá trình deserialization bằng cách sử dụng FacesContext. Tôi không nghĩ rằng sẽ làm việc kể từ khi FacesContext chỉ có sẵn trong suốt tuổi thọ của một yêu cầu. Việc deserialization đang xảy ra lúc khởi động ứng dụng. –

+2

đúng, đó là lý do tôi xóa câu trả lời của mình. Nó xuất hiện phức tạp hơn (do đó +1 cho câu hỏi) – Bozho

Trả lời

5

Mặc dù giải pháp được Bozho cung cấp có thể hoạt động, tôi không muốn giới thiệu các đối tượng proxy vào một ứng dụng hiện không sử dụng chúng. Giải pháp của tôi là ít hơn lý tưởng, nhưng nó được công việc làm.

tôi rời lĩnh vực thoáng qua tại chỗ:

transient private ApplicationData _applicationData; 

Tôi cũng rời setter tại chỗ để JSF ban đầu có thể thiết lập các tài liệu tham khảo khi đối tượng SessionData được tạo ra lần đầu tiên:

public void setApplicationData(ApplicationData applicationData) { 
    _applicationData = applicationData; 
} 

Thay đổi mà tôi đã thực hiện là trong phương thức getter. Các phương thức trong đối tượng SessionData bây giờ cần phải dừng trực tiếp truy cập vào trường _applicationData và thay vào đó lấy tham chiếu qua bộ thu thập. Đầu tiên, getter sẽ kiểm tra một tham chiếu null. Nếu nó là null, thì bean được quản lý thu được thông qua FacesContext. Ràng buộc ở đây là FacesContext chỉ khả dụng trong suốt tuổi thọ của yêu cầu.

/** 
* Get a reference to the ApplicationData object 
* @return ApplicationData 
* @throws IllegalStateException May be thrown if this method is called 
* outside of a request and the ApplicationData object needs to be 
* obtained via the FacesContext 
*/ 
private ApplicationData getApplicationData() { 
    if (_applicationData == null) { 
     _applicationData = JSFUtilities.getManagedBean(
      "applicationData", // name of managed bean 
      ApplicationData.class); 
     if (_applicationData == null) { 
      throw new IllegalStateException(
       "Cannot get reference to ApplicationData object"); 
     } 
    } 
    return _applicationData; 
} 

Nếu bất cứ ai quan tâm, đây là mã cho phương pháp getManagedBean() tôi:

/** 
* <p>Retrieve a JSF managed bean instance by name. If the bean has 
* never been accessed before then it will likely be instantiated by 
* the JSF framework during the execution of this method.</p> 
* 
* @param managedBeanKey String containing the name of the managed bean 
* @param clazz Class object that corresponds to the managed bean type 
* @return T 
* @throws IllegalArgumentException Thrown when the supplied key does 
* not resolve to any managed bean or when a managed bean is found but 
* the object is not of type T 
*/ 
public static <T> T getManagedBean(String managedBeanKey, Class<T> clazz) 
     throws IllegalArgumentException { 
    Validate.notNull(managedBeanKey); 
    Validate.isTrue(!managedBeanKey.isEmpty()); 
    Validate.notNull(clazz); 
    FacesContext facesContext = FacesContext.getCurrentInstance(); 
    if (facesContext == null) { 
     return null; 
    } 
    Validate.notNull(facesContext.getApplication()); 
    ELResolver resolver = facesContext.getApplication().getELResolver(); 
    Validate.notNull(resolver); 
    ELContext elContext = facesContext.getELContext(); 
    Validate.notNull(elContext); 
    Object managedBean = resolver.getValue(
     elContext, null, managedBeanKey); 
    if (!elContext.isPropertyResolved()) { 
     throw new IllegalArgumentException(
      "No managed bean found for key: " + managedBeanKey); 
    } 
    if (managedBean == null) { 
     return null; 
    } else { 
     if (clazz.isInstance(managedBean)) { 
      return clazz.cast(managedBean); 
     } else { 
      throw new IllegalArgumentException(
       "Managed bean is not of type [" + clazz.getName() + 
       "] | Actual type is: [" + managedBean.getClass().getName()+ 
       "]"); 
     } 
    } 
} 

Và đừng chọn vào Validate tôi gọi. Tôi sẽ lấy chúng ra sau khi tôi hoàn thành công việc phát triển! :)

+4

Có một phím tắt trong ['Application # evaluationExpressionGet()'] (http://download.oracle.com/javaee/6/api/javax/faces/application/ Application.html # evaluationExpressionGet% 28javax.faces.context.FacesContext,% 20java.lang.String,% 20java.lang.Class% 29). Xem thêm [câu trả lời này] (http://stackoverflow.com/questions/2633112/jsf-get-managed-bean-by-name/2633733#2633733). – BalusC

1

bạn có thể thêm một phương pháp:

private void readObject(java.io.ObjectInputStream in) 
throws IOException, ClassNotFoundException { 
    in.defaultReadObject(); 
    applicationData = initializeApplicationData(); 
} 

Và trong initializeApplicationData bạn có thể sử dụng một đối tượng proxy động. Sử dụng CGLIB hoặc javassist tạo một proxy, trước khi mỗi lời gọi phương thức thiết lập một trường nội bộ - ApplicationData thực. Nếu đó là null, sau đó nhận được dòng điện FacesContext (mà sẽ có thể truy cập vào thời điểm đó) và nhận được bean được quản lý từ đó theo:

FacesContext facesContext = FacesContext.getCurrentInstance(); 
originalApplicationData = (ApplicationData)facesContext.getApplication() 
    .createValueBinding("#{applicationData}").getValue(facesContext); 

và đại biểu đến phương pháp của nó.

Đây là giải pháp xấu, nhưng tôi nghĩ nó sẽ hoạt động.

+0

Đây sẽ là lần đầu tiên tôi sử dụng javassist, vì vậy tôi sẽ cần một chút thời gian để tiêu hóa phần đó của giải pháp của bạn. Cảm ơn bạn. –

+0

nó khá dễ dàng. Chỉ cần làm theo hướng dẫn - trường hợp proxy của bạn là đơn giản nhất :) – Bozho

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