簡體   English   中英

JSF - 會話范圍的托管bean沒有在會話反序列化上重新注入依賴項

[英]JSF - Session-scoped managed bean does not have dependencies re-injected on session deserialization

我不確定我做的是不是錯了,或者我錯過了某個地方的注釋或配置項。 情況如下:

我有一個JSF應用程序,其中包含一個名為SessionData的會話范圍的bean。 這個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對象:

transient private ApplicationData applicationData;

一切都很好,直到Web應用程序停止(在我的Tomcat 6.x容器中)並且會話被序列化。 當我重新啟動應用程序並反序列化會話時,JSF不會重新注入我對ApplicationData引用。 我知道反序列化應該留下沒有值的瞬態字段。 有沒有辦法告訴JSF這個會話范圍的對象要求在反序列化后再次設置其依賴項?

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

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

我把瞬態場留在了原地:

transient private ApplicationData _applicationData;

我也將setter留在原位,因此JSF可以在第一次創建SessionData對象時初始設置引用:

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

我做的改變是在getter方法中。 SessionData對象中的方法現在需要停止直接訪問_applicationData字段,而是通過getter獲取引用。 getter將首先檢查null引用。 如果為null,則通過FacesContext獲取托管bean。 這里的約束是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()+
                "]");
        }
    }
}

並且不要選擇我的驗證電話。 在完成開發之后我會把它們拿出來! :)

你可以添加一個方法:

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

並委托其方法。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM