通过 @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 提供的解决方案,最初它似乎有效。和原来的问题一样,我也有多个支持的语言环境,用户可以在语言环境之间切换(在我的例子中是 ca
和 es
)
我在 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");
// ...
}
}
如何从 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
.
您需要使用 @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 提供的解决方案,最初它似乎有效。和原来的问题一样,我也有多个支持的语言环境,用户可以在语言环境之间切换(在我的例子中是 ca
和 es
)
我在 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");
// ...
}
}