Java JSF-会话范围的托管bean没有在会话反序列化时重新注入依赖项

Java JSF-会话范围的托管bean没有在会话反序列化时重新注入依赖项,java,jsf,serialization,dependency-injection,httpsession,Java,Jsf,Serialization,Dependency Injection,Httpsession,我不确定我所做的是否是错误的,或者我是否只是遗漏了某个注释或配置项。情况如下: 我有一个JSF应用程序,它有一个名为SessionData的会话范围bean。这个bean在创建时有一个应用程序范围的bean引用(类型为ApplicationData)。第一次创建会话时,该操作正常。依赖项注入是通过faces config.xml文件中的元素完成的,如下所示: <managed-bean> <managed-bean-name>sessionData</man

我不确定我所做的是否是错误的,或者我是否只是遗漏了某个注释或配置项。情况如下:

我有一个JSF应用程序,它有一个名为
SessionData
的会话范围bean。这个bean在创建时有一个应用程序范围的bean引用(类型为
ApplicationData
)。第一次创建会话时,该操作正常。依赖项注入是通过
faces config.xml
文件中的
元素完成的,如下所示:

<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>
在web应用程序停止(在我的Tomcat6.x容器中)并序列化会话之前,一切都很好。当我重新启动应用程序并反序列化会话时,JSF不会重新注入对
ApplicationData
的引用。我知道反序列化应该使瞬态字段没有值是否有一种方法可以向JSF发出信号,表明此会话作用域对象需要在反序列化后再次设置其依赖项?

我使用MyFaces JSF 1.2和Tomcat 6.0.26作为我的web应用程序容器。

您可以添加一个方法:

private void readObject(java.io.ObjectInputStream in)
 throws IOException, ClassNotFoundException {
  in.defaultReadObject();
  applicationData = initializeApplicationData();
}
initializeApplicationData
中,可以使用动态代理对象。使用CGLIB或javassist创建一个代理,该代理在每次方法调用之前设置一个内部字段,即real
ApplicationData
。如果为
null
,则获取当前的
FacesContext
(此时可以访问),并通过以下方式从中获取托管bean:

FacesContext facesContext = FacesContext.getCurrentInstance();
originalApplicationData = (ApplicationData)facesContext.getApplication()
  .createValueBinding("#{applicationData}").getValue(facesContext);
并委托给它的方法


这是一个丑陋的解决方案,但我认为它会起作用。

尽管Bozho提供的解决方案可以起作用,但我不想将代理对象引入到当前未使用它们的应用程序中。我的解决方案不太理想,但它完成了任务

我将瞬态场留在原位:

transient private ApplicationData _applicationData;
我还保留了setter,以便JSF可以在第一次创建
SessionData
对象时首先设置引用:

public void setApplicationData(ApplicationData applicationData) {
    _applicationData = applicationData;
}
我所做的改变是在getter方法中。
SessionData
对象中的方法现在需要停止直接访问
\u applicationData
字段,而是通过getter获取引用。getter将首先检查空引用。如果为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()+
                "]");
        }
    }
}
/**
*按名称检索JSF托管bean实例。如果豆子有
*在此之前从未被访问过,它很可能由
*在执行此方法的过程中,JSF框架

* *@param managedBeanKey字符串,包含托管bean的名称 *与托管bean类型对应的@param clazz类对象 *@return T *@throws-IllegalArgumentException在提供的密钥不存在时抛出 *不解析为任何托管bean,或者在找到托管bean时,但 *该对象不是T类型 */ 公共静态T getManagedBean(字符串managedBeanKey,类clazz) 抛出IllegalArgumentException{ Validate.notNull(managedBeanKey); Validate.isTrue(!managedBeanKey.isEmpty()); Validate.notNull(clazz); FacesContext FacesContext=FacesContext.getCurrentInstance(); if(facesContext==null){ 返回null; } Validate.notNull(facesContext.getApplication()); ELResolver resolver=facesContext.getApplication().getELResolver(); Validate.notNull(解析器); ELContext ELContext=facesContext.getELContext(); Validate.notNull(elContext); 对象managedBean=resolver.getValue( elContext,null,managedBeanKey); 如果(!elContext.isPropertyResolved()){ 抛出新的IllegalArgumentException( 找不到密钥的托管bean:“+managedBeanKey”); } if(managedBean==null){ 返回null; }否则{ if(类实例(managedBean)){ 返回clazz.cast(managedBean); }否则{ 抛出新的IllegalArgumentException( “托管bean的类型不是[”+clazz.getName()+ “]|实际类型为:[”+managedBean.getClass().getName()+ "]"); } } }

不要挑我的电话。完成开发后,我会将它们取出!:)

有人建议我提供一个readObject()方法,并在反序列化期间使用FacesContext在其中手动设置ApplicationData对象。我认为这不会起作用,因为FacesContext只在请求的生命周期内可用。反序列化正在应用程序启动时进行。正确,这就是我删除答案的原因。它看起来更复杂(因此问题是+1),这将是我第一次使用javassist,所以我需要一些时间来消化解决方案的这一部分。谢谢,很简单。只需遵循教程-您的代理案例是最简单的:)中有一个快捷方式。另见。
/**
 * <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()+
                "]");
        }
    }
}