通过 @ManagedProperty 注入 ResourceBundle 似乎在 @Named 中不起作用

Injecting ResourceBundle via @ManagedProperty doesn't seem to work inside @Named

如何从 java 代码访问消息包以根据当前语言环境获取消息?

我尝试使用 @ManagedProperty 如下所示:

@Named 
@SessionScoped 
public class UserBean implements Serializable {

    @ManagedProperty("#{msg}")
    private ResourceBundle bundle;
    
    // ...

    public void setBundle(ResourceBundle bundle) {
        this.bundle = bundle;
    }

}

然而,它仍然是 null。它似乎在 @Named.

中不起作用

这就是我在 faces-context.xml 中注册资源包的方式:

<application>
    
    <message-bundle>validator.messages</message-bundle>
    
    <locale-config>
        <supported-locale>en_US</supported-locale>
        <supported-locale>ua_UA</supported-locale>
    </locale-config>
    
    <resource-bundle>
        <base-name>lang.messages</base-name>
        <var>msg</var>
    </resource-bundle>
    
</application>

作者更新:

我收到错误

16:29:10,968 ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/WEBSearchPrime_JB_lang].[Faces Servlet]] (http-localhost-127.0.0.1-8080-1) Servlet.service() for servlet Faces Servlet threw exception: org.jboss.weld.exceptions.IllegalProductException: WELD-000054 Producers cannot produce non-serializable instances for injection into non-transient fields of passivating beans\n\nProducer\: Producer Method [PropertyResourceBundle] with qualifiers [@Any @Default] declared as [[method] @Produces public util.BundleProducer.getBundle()]\nInjection Point\: [field] @Inject private model.UserBean.bundle

注意,我还放置了 Serializable 接口

您不能在使用 @Named 注释的 CDI 托管 bean 中使用 @javax.faces.bean.ManagedProperty。您只能在用 @ManagedBean.

注释的 JSF 托管 bean 中使用它

您需要使用 @javax.faces.annotation.ManagedProperty 以及 @Inject。这是在 JSF 2.3 中引入的。

@Inject @javax.faces.annotation.ManagedProperty("#{msg}")
private ResourceBundle bundle;

请注意,这将作为 @Dependent 注入。所以请注意,当您将其注入 @SessionScoped bean 时,它基本上也会变成 @SessionScoped 并因此永远坚持最初注入的值。因此,稍后在会话中发生的任何潜在区域设置更改都不会反映在此处。如果这是一个拦截器,那么你真的应该只将它注入 @RequestScoped@ViewScoped,或者使用 @Producer,如下所示。

CDI 没有用于注入 EL 表达式的计算结果的本机注释。 CDI 方法使用 "CDI producer" 和 @Produces,其中 return 具体类型,在 .properties 基于文件的资源包的情况下是 PropertyResourceBundle

所以,如果您无法升级到 JSF 2.3,那么只需将此 class 放在您的 WAR:

中的某处
@RequestScoped
public class BundleProducer {

    @Produces
    public PropertyResourceBundle getBundle() {
        FacesContext context = FacesContext.getCurrentInstance();
        return context.getApplication().evaluateExpressionGet(context, "#{msg}", PropertyResourceBundle.class);
    }

}

有了这个,可以像下面这样注入:

@Inject
private PropertyResourceBundle bundle;

另外BalusC的回答:

从 JSF 2.3 开始,还可以在不使用生产者方法的情况下注入 faces-config.xml 中定义的资源包。有一个新的注释 javax.faces.annotation.ManagedProperty(注意它在 ...annotation 包中,而不是 ...bean 包中)与 @Inject:

一起使用
// ...
import javax.faces.annotation.ManagedProperty;
// ...

@Named 
@SessionScoped 
public class UserBean implements Serializable {

  // ...

  @Inject
  @ManagedProperty("#{msg}")
  private ResourceBundle bundle;

  // ...

}

也许我弄错了,但实际上提供的两种解决方案都不适用于我的用例。所以我提供了另一个对我有用的答案。

根据BalusC提供的答案,我遇到了以下问题:

  • 因为我使用的是基于 ajax 的验证,所以我的 bean 是 @ViewScoped,并且 必须是可序列化的。因为既不是 ResourceBundle 也不是 PropertyResourceBundle 是可序列化的,它们不能被注入 @Dependent 范围。

  • 如果我尝试将生产者更改为使用 @RequestScoped,它也会失败 因为 ResourceBundle 不可代理,因为它定义 final 方法。

因为我使用的是 JSF 2.3 (JBoss 7.2EAP),所以我使用了 Jören Haag 提供的解决方案,最初它似乎有效。和原来的问题一样,我也有多个支持的语言环境,用户可以在语言环境之间切换(在我的例子中是 caes

我在 Jören 的回答中遇到的问题是

  • 返回的ResourceBundle是在创建bean时求值的。在他的示例中,他使用了 @SessionScoped bean。 ResourceBundle 将在创建会话时使用当前语言环境进行解析。如果用户稍后更改语言环境,ResourceBundle 将不会更新。如果用户可以在不更改视图的情况下更改语言,@ViewScoped bean 也会发生这种情况。

  • 我还遇到了另一个需要使用 <f:viewAction> 预加载数据的 bean 问题。在这种情况下,bean 被更早地实例化,因此 ResourceBundle 在用户区域设置 <f:view locale="#{sessionBean.locale}"> 之前被注入。因此,如果用户浏览器使用 es,但用户将语言环境更改为 ca,则将使用 es 加载包,因为视图语言环境未设置为 [=19] =] 与 sessionBean.locale 直到渲染阶段。

为了克服这个问题,对于使用注入的问题的用例,对我有用的解决方案是:

@SessionScoped
public class UserBean implements Serializable {

    @Inject
    @ManagedProperty("#{msg}")
    private Instance<ResourceBundle> bundle;

    // ....

    public void someAction() {
        String message = bundle.get().getString("someLabel");
        // ...
    }

}

由于原题不需要注入,只问如何访问当前语言环境的资源包,无需注入的解决方案,并且没有每次评估EL表达式的开销#{msg} bundle.get() 被称为:

@SessionScoped
public class UserBean implements Serializable {

    @Inject
    private FacesContext context;

    // ...

    public void someAction() {
        ResourceBundle bundle = context.getApplication().getResourceBundle(context, "msg");
        String message = bundle.getString("someLabel");
        // ...
    }

}