【问题标题】:Injecting non-serializable application scoped bean as managed property of serializable session scoped bean in a cluster将不可序列化的应用程序范围 bean 作为集群中可序列化会话范围 bean 的托管属性注入
【发布时间】:2013-11-19 09:27:40
【问题描述】:

我有以下托管 bean:

@ApplicationScoped
public class ApplicationBean {
    // ...
}
@SessionScoped
public class SessionBean implements Serializable {

    @ManagedProperty("#{applicationBean}")
    private ApplicationBean applicationBean;

    // ...
}

这被部署到具有多个节点的服务器集群。当 HTTP 会话将在另一个节点上序列化时会发生什么?

ApplicationBean 没有序列化,因为它没有实现Serializable。会被@ManagedProperty重新注入吗?或者它实际上会以某种方式被序列化?

【问题讨论】:

    标签: jsf session cluster-computing


    【解决方案1】:

    当 HTTP 会话在另一个节点上序列化时会发生什么?

    所有 HTTP 会话属性也将被序列化,包括会话范围的 JSF 托管 bean。任何不可序列化的 bean 属性都将被跳过。在另一个节点上反序列化期间,您将在所有不可序列化的 bean 属性上遇到NotSerializableException。标记属性transient 将修复该异常,但在反序列化后该属性仍将保留null


    @ManagedProperty 会重新注入它吗?或者它实际上会以某种方式被序列化?

    不。它不会被重新注入。如果是@ManagedProperty,您必须手动处理此问题。

    一种有点幼稚且容易出错的方法是摆脱 @ManagedProperty 并在 getter 中执行延迟加载(因此,您自己就像代理一样):

    private transient ApplicationBean applicationBean;
    
    public ApplicationBean getApplicationBean() {
        if (applicationBean == null) { 
            FacesContext context = FacesContext.getCurrentInstance();
            applicationBean = context.getApplication().evaluateExpressionGet(context, "#{applicationBean}", ApplicationBean.class);
        }
    
        return applicationBean;
    }
    

    并在整个代码中使用 getter,而不是直接引用属性。

    更好的方法是使其成为 EJB 或 CDI 托管 bean。它们是完全透明地创建并作为可序列化代理注入的,您无需担心它们的序列化。

    因此,要么将其设为 EJB:

    import javax.ejb.Singleton;
    
    @Singleton
    public class ApplicationBean {
        // ...
    }
    
    import javax.ejb.EJB;
    import.javax.faces.bean.ManagedBean;
    import.javax.faces.bean.SessionScoped;
    
    @ManagedBean
    @SessionScoped
    public class SessionBean implements Serializable {
    
        @EJB
        private ApplicationBean applicationBean;
    
        // ... (no setter/getter necessary!)
    }
    

    或者,将它们都设为 CDI 托管 bean:

    import javax.enterprise.context.ApplicationScoped;
    import javax.inject.Named;
    
    @Named
    @ApplicationScoped
    public class ApplicationBean {
        // ...
    }
    
    import javax.enterprise.context.SessionScoped;
    import javax.inject.Inject;
    import javax.inject.Named;
    
    @Named
    @SessionScoped
    public class SessionBean implements Serializable {
    
        @Inject
        private ApplicationBean applicationBean;
    
        // ... (also here, no setter/getter necessary!)
    }
    

    【讨论】:

      【解决方案2】:

      我也遇到了类似的问题。

      我有一个每 x 秒运行一次的 Quartz 调度程序。

      Quartz 的 Job 更新了我的应用程序范围 bean 中的项目列表。

      我使用下面显示的静态方法获取应用程序 bean。

      当我部署应用程序时,它运行良好,但当用户进入网络应用程序时,FacesContext.getCurrentInstance() 总是返回 null。

          public static <T> T getBean(final String beanName, final Class<T> beanClass) {
              FacesContext facesContext = FacesContext.getCurrentInstance();
              T result = null;
              if (facesContext != null) {
                  Application application = facesContext.getApplication();
                  if (application != null) {
                      String expresionBeanName = "#{" + beanName + "}";
                      result = application.evaluateExpressionGet(facesContext, expresionBeanName, beanClass);
                  }
              }
              return result;
          }
      
          @ManagedBean
          @ApplicationScoped
          public class ApplicationBean implements Serializable { ... }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-02-26
        • 1970-01-01
        • 2012-06-27
        • 2015-09-17
        • 1970-01-01
        • 2022-10-24
        • 2015-07-31
        • 1970-01-01
        相关资源
        最近更新 更多