【问题标题】:Accessing ResourceBundle as ManagedProperty Serialization Issue访问 ResourceBundle 作为 ManagedProperty 序列化问题
【发布时间】:2015-10-03 19:30:35
【问题描述】:

首先,对不起我的英语不好!

在以下托管 Bean (ApplicationScoped) 中,我将 ResourceBundle(.properties) 作为 @ManagedProperty 访问。 ResourceBundle 对象不可序列化,因此我在 Eclipse/Tomcat 控制台中收到一个错误消息,指出该对象无法序列化/反序列化.. 等等。

从持久存储中加载异常会话 java.io.WriteAbortedException:写入中止; java.io.NotSerializableException: java.util.PropertyResourceBundle

我对这个问题有 2 个问题:

  • 我认为,JSF 将预定义(在 faces-config.xml 中)ResourceBundles 处理为 ApplicationScoped bean。这意味着(如果我理解正确的话),这个 Object/Bean(ResourceBundle)以某种方式存储在文件中的某个地方(持久存储)。现在,既然ResourceBundle 不可序列化,那么它以哪种格式存储? JSF 如何为这样的“Beans”服务?序列化的 Objects 以 Bytes 的形式存储在文件中,那么不可序列化的 Objects 是如何存储的呢?
  • 在下面的示例中,我将我的@ManagedProperty ResourceBundle 声明为transient(由于序列化问题),但是瞬态对象不会存储在持久存储中(无状态),这是否意味着每次调用方法getConfigurationAttribute(我在哪里使用这个resourceBundle)都会重新创建/重新加载ManagedPropery ResourceBundle,因为它被标记为瞬态?

非常感谢您的帮助。

@ManagedBean(name="facesResource",eager=true)
@ApplicationScoped
public class FacesResource implements Serializable{
    private static final long serialVersionUID = 2454454363100273885L;
    @ManagedProperty("#{FACES_CONFIG}")
    private ResourceBundle facesConfig;
    //private transient ResourceBundle facesConfig;

    ....
    private Map<String,Language> languagesMap;  
    private Map<String,Theme> themesMap;
    ....

    public FacesResource(){

    }
    @PostConstruct
    public void init(){
        System.out.println("*** FacesResource init ....");
        try{
            ....
            this.initLanguages();
            this.initThemes();
            ....
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }       

    public String getConfigurationAttribute(String attributeKey){
        return this.facesConfig.getString(attributeKey);
    }
    // ... other methods & getter/setter ...etc

}

更新:

  • FacesResource Bean 中的 ResourceBundle 独立 独立于请求 Locale,因此在 ApplicationScoped Bean 中加载它不是问题,但是

  • 因为我在其他 SessionScoped Bean 中访问/注入(作为 @ManagedProperty)这个 ApplicationScoped Bean,它应该被序列化,这意味着所有属性(包括资源包)也应该被序列化,这里我遇到了问题带序列化/反序列化

  • @BalusC:如果我确实喜欢你在回答中的建议:ResourceBundle.getBundle("com.example.text"),我必须提供捆绑包的基本名称。但这正是我想要避免的。我不想在 java 源代码中硬编码静态路径),所以当路径发生变化时(最不可能,但对于这种情况),我不喜欢在 Java 源代码中更改路径,而只在 faces-config 中更改路径。 xml。

  • 我不能使用FacesContext.getCurrentInstance().getApplication().getResourceBundle(facesContext, "bundleVarName");,因为我的 Bean 被标记为 eager=true,这意味着,facesContext 目前是 NULL

【问题讨论】:

    标签: jsf serialization jsf-2.2 resourcebundle transient


    【解决方案1】:

    您不能在不可序列化的类型上使用@ManagedProperty。

    它是本地化字符串的资源包吗?

    阅读:http://www.mkyong.com/jsf2/jsf-2-0-and-resource-bundles-example/

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ResourceBundle resourceBundle = facesContext.getApplication()
                                           .getResourceBundle(facesContext, "bundleName");
    

    【讨论】:

    • 谢谢,但是从 JSF 2.2 开始,我可以以 @ManagedProperty 的身份访问 ResourceBundle!
    • 哦,我没有看到标签“jsf-2.2”。我对 2.2 版本了解不多,但我坚持在不可序列化类型上使用 @ManagedProperty。我认为您只能访问映射在 faces-config.xml 上的 bundle 的单个属性。 "@ManagedProperty("#{bundleVar['property_text']}") 字符串文本"。您确定 Type 可以直接访问吗?您从哪里读到有关以这种方式访问​​ ResourceBundle 的信息?我想了解它:)
    • 是的,类型可以直接访问,因为@ManagedProperty 我对此进行了测试。但是,如果这些 Bundle 依赖于 Locale(在我的情况下不是),那么它在 requestScoped Beans 中唯一的好做法。在我的情况下,它会导致序列化问题,因为我在 ApplicationScoped Bean 中声明它,然后我将此 Bean 注入其他必须可序列化的 SessionScoped bean。所以所有属性(包括 ResourceBundle)都应该被序列化,这就是问题所在!
    【解决方案2】:

    我认为,JSF 将预定义的(在 faces-config.xml 中)ResourceBundles 处理为 ApplicationScoped bean。

    不。它们由ResourceBundle API 本身管理。 JSF 只是根据请求的语言环境在每个请求的基础上解析它们(否则它会影响访问 Web 应用程序的任何用户的语言!)。因此,它们本质上是请求范围的。但这一切都与序列化无关。 ResourceBundle 类根本不打算被序列化。它只是在 Java 内存中延迟加载包。

    你最好也这样做。如果反序列化后变为null,则延迟加载。您不应该只评估#{FACES_CONFIG},因为它取决于请求区域设置。如果您只能使用 JSF &lt;resource-bundle&gt;&lt;var&gt;,那么您最好通过 Application#getResourceBundle() 加载它们。提供了 FACES_CONFIG 的资源包 var 名称,这是一个示例:

    private transient ResourceBundle facesConfig;
    
    public ResourceBundle getFacesConfig() {
        if (facesConfig == null) {
            FacesContext context = FacesContext.getCurrentInstance();
            facesConfig = context.getApplication().getResourceBundle(context, "FACES_CONFIG");
        }
    
        return facesConfig; 
    }
    

    顺便说一句,变量名facesConfig 很混乱。即暗示它代表faces-config.xml的内容。

    另见:

    【讨论】:

    • 感谢 BalusC,我看到了您的回答 stackoverflow.com/questions/6272945/…,并且您建议我们可以将 ResourceBundle 用作 ManagedProperty,它是否只对 requestScoped Beans 有好处,因为它在 requestScope 中加载后每次都加载?名称 FACES_CONFIG 与项目名称 (YouFaces) 相关,而不是 faces-config.xml。
    • 由于资源包是请求范围的,资源包上的@ManagedProperty 确实只在请求范围的 bean 中有效。然而,CDI @Inject 几乎没有定制生产者的帮助,这是可能的。根据您的问题更新,您应该能够在@PostConstruct 中获得FacesContext,即使在急切初始化的应用程序范围的bean 中也是如此。我更新了答案,只是在延迟加载 getter 中完成这项工作。
    • 非常感谢 BalusC。请您向我解释为什么/如何在没有任何请求之前/没有任何请求时使用 FacesContext 吗?我认为 FacesContext 仅在第一次请求后才可用。
    • 在启动期间有一个特殊的“init”FacesContext。它将在 a.o 中准备好所有应用程序范围的东西。 getApplication(),但例如ExternalContext 的方法在那时可能确实没有达到预期的效果。但无论如何,在这种情况下你不需要它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-29
    • 2010-12-12
    • 1970-01-01
    相关资源
    最近更新 更多