2010-09-23 57 views
8

我不知道我所做的是錯誤的,還是我錯過了某處的註釋或配置項目。這裏的情況:JSF - 會話範圍的託管bean在會話反序列化中沒有重新注入依賴關係

我有一個JSF應用程序,其會話範圍bean名爲SessionData。這個bean在創建時注入了一個應用程序範圍的bean引用(類型爲ApplicationData)。當會話第一次創建時,這工作正常。如下所示的依賴注入與在faces-config.xml文件<managed-bean>元素完成:

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

因爲它沒有任何意義有我SessionData對象包括ApplicationData對象時,它是序列化,我已經標誌着ApplicationData參考瞬態在我SessionData對象:直到web應用程序被停止(在我的Tomcat 6.x的容器)和會話序列化

transient private ApplicationData applicationData; 

一切都很好。當我重新啓動應用程序並且會話被反序列化時,我對於ApplicationData的引用不會被JSF重新注入。我知道反序列化應該會讓瞬態領域沒有價值。 有沒有一種方法可以告知JSF,這個會話範圍對象需要在反序列化之後再次設置它的依賴關係?

我使用MyFaces JSF 1.2和Tomcat 6.0.26作爲我的Web應用程序容器。

+1

有人建議我提供一個readObject()方法,並通過使用FacesContext在反序列化期間手動設置ApplicationData對象。我不認爲這會起作用,因爲FacesContext僅在請求的生命週期中可用。反序列化發生在應用程序啓動時。 – 2010-09-23 12:47:13

+2

正確,這就是爲什麼我刪除了我的答案。它似乎更復雜(因此問題+1) – Bozho 2010-09-23 12:56:28

回答

5

儘管Bozho提供的解決方案可以工作,但我不想將代理對象引入到當前未使用它們的應用程序中。我的解決方案並不理想,但它完成了工作。

餘留在原處的瞬態場:

transient private ApplicationData _applicationData; 

我也離開了設定器在適當位置,以便JSF可以初始設定的基準時,SessionData對象被創建在第一時間:

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

我做的改變是在getter方法中。 SessionData對象中的方法現在需要停止直接訪問_applicationData字段,而是通過getter獲取引用。吸氣劑將首先檢查空引用。如果它爲空,則管理bean通過FacesContext獲得。這裏的限制是FacesContext僅在請求的使用期限內可用。

/** 
* 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; 
} 

如果有人問津,這裏是我的getManagedBean()方法的代碼:

/** 
* <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()+ 
       "]"); 
     } 
    } 
} 

而且不接我的電話驗證。我完成了開發後我會把它們取出來! :)

+4

['Application#evaluateExpressionGet()']中有一個快捷方式(http://download.oracle.com/javaee/6/api/javax/faces/application/ Application.html#evaluateExpressionGet%28javax.faces.context.FacesContext,%20java.lang.String,%20java.lang.Class%29)。另見[這個答案](http://stackoverflow.com/questions/2633112/jsf-get-managed-bean-by-name/2633733#2633733)。 – BalusC 2010-09-23 15:57:35

1

,你可以添加一個方法:

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

而且在initializeApplicationData您可以使用動態代理對象。使用CGLIB或javassist創建一個代理,在每個方法調用設置一個內部字段之前 - 真正的ApplicationData。如果是null,然後獲取當前FacesContext(這將是在這一點上訪問),並從那裏得到託管bean:

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

,並委託給它的方法。

這是一個醜陋的解決方法,但我認爲它會起作用。

+0

這將是我第一次使用javassist,所以我需要一些時間來消化你的解決方案的一部分。謝謝。 – 2010-09-23 13:31:41

+0

這很容易。只需按照教程 - 您的代理案例是最簡單的:) – Bozho 2010-09-23 13:37:17

相關問題