如何使用 Weld 注入不可序列化的类(如 java.util.ResourceBundle)
Posted
技术标签:
【中文标题】如何使用 Weld 注入不可序列化的类(如 java.util.ResourceBundle)【英文标题】:How to inject a non-serializable class (like java.util.ResourceBundle) with Weld 【发布时间】:2011-03-01 07:41:15 【问题描述】:我想创建一个生产者,可以将 java.util.ResourceBundle 注入到任何类中,以便轻松获取本地化字符串。我的 ResourceBundle-Producer 如下所示:
public class ResourceBundleProducer
@Inject
public Locale locale;
@Inject
public FacesContext facesContext;
@Produces
public ResourceBundle getResourceBundle()
return ResourceBundle.getBundle("/messages", locale )
Locale 和 FacesContext 的注入工作(从 Seam 3 Alpha Source 获取相应的生产者)。但不幸的是,ResourceBundle 不是可序列化的,因此不能以这种方式生成。尝试访问调用使用我的 ResourceBundle 的 bean 的 JSF 页面时,我从 Weld 收到以下错误:
Caused by: org.jboss.weld.IllegalProductException: WELD-000054 Producers cannot produce non-serializable instances for injection into non-transient fields of passivating beans\\n\\nProducer\: org.jboss.weld.bean-/D:/Program Files (x86)/GlassFish-Tools-Bundle-For-Eclipse-1.2/glassfishv3/glassfish/domains/teachernews/applications/teachernews/-ProducerMethod-services.producers.ResourceBundleProducer.getResourceBundle()\\nInjection Point\: field web.PersonHome.bundle
有什么方法可以让我的 ResourceBundleResolver 工作?还是有任何其他机制可以获得类似的功能? 提前致谢!
编辑:
好的,我会花掉一些我辛苦赚来的积分;) 也将接受此问题的良好解决方法!
我得到了另一个无法创建 Producer 的示例:FlashProducer。 FacesContext-Flash 也无法生成,因为 Flash 不可序列化。
【问题讨论】:
【参考方案1】:嗯,首先 ResourceBundle 不可序列化。见here。而且信息很明确
无法生成不可序列化实例以注入到钝化bean的非瞬态字段
钝化 bean ???我认为 web.PersonHome 是有状态会话 Bean 或 @ConversationScoped bean。我对吗 ???如果是这样,您应该将您的 bundle 属性标记为瞬态
private transient @Inject ResourceBundle bundle;
【讨论】:
@ifischer 免责声明:我还没有使用 Weld。我使用接缝。所以我不确定@Inject 注解是否可以与瞬态一起使用 恭喜,您刚刚为自己赢得了 50 分;)终于找到了解决方案!想知道为什么花了这么长时间+赏金,因为这很容易(也想知道为什么我自己没有发现......)。刚刚测试过,也适用于 CDI/Weld。现在我终于可以注入 Resourcebundles 和 Contexts 了。 @ifischer 你是对的。因为 ConversationScoped 必须在请求之间可序列化,所以它的所有字段都必须实现 Serializable 接口除非您将其标记为瞬态。它解释了为什么您会收到错误消息。有状态会话 bean 也会出现同样的问题。 但是bundle
标记为transient
,您如何确定它在您需要时可用?在 bean 钝化/激活的情况下,ResourceBundle
消失了,访问它会引发异常。
@Sam 但 CDI 注入仅在创建 bean 时完成一次。因此,“容器”(即 Weld)在钝化/激活后不会重新检查其字段的值。钝化/激活后访问该字段将导致 NullPointerException。【参考方案2】:
根据 Arthur 接受的答案中的 cmets 线程。我跟着this blog和this one进行了钝化/激活实验。实验证明了 MrD 的评论,即瞬态属性在激活时将为 NULL。因此,为了处理具有钝化能力的 bean(即 sessionscoped、conversationscoped 和 stateful session bean)的不可序列化成员属性,我建议使用以下解决方案:
private ResourceBundle bundle;
@PostConstruct
@PostActivate
public void getResourceBundle()
bundle = ResourceBundle.getBundle("/messages", locale );
此解决方案确保每次进入 READY 状态时都重新初始化不可序列化的属性成员。
最后一个要解决的问题
最后一个要解决的问题是注入在 slf4j 1.5.3 之前不可序列化的 SLF4j Logger 和 I quote:
从 SLF4J 版本 1.5.3 开始,记录器实例在序列化后仍然存在。 因此,主机类的序列化不再需要任何特殊的 操作,即使记录器被声明为实例变量。
因此,只要您的 slf4j 依赖项是 1.5.3 或更高版本,您就可以安全地注入 SLF4j Logger,如下所示:
@Produces
@LogbackLogger
public Logger produceLogger(InjectionPoint injectionPoint)
return LoggerFactory.getLogger(injectionPoint.getMember().getDeclaringClass().getName());
假设您声明了限定符:
@Qualifier
@Retention(RUNTIME)
@Target(METHOD, FIELD, PARAMETER, TYPE)
public @interface LogbackLogger
然后在一个支持钝化的bean中,注入如下:
@Inject
@LogbackLogger
private Logger logger;
【讨论】:
关于ResourceBundle
的“生产者”:如果我理解正确,你不会使用ResourceBundle
的生产者 - 失去了 CDI 的魅力.相反,您在 PostConstruct 和 -Activate 事件上手动获取ResourceBundle
(顺便说一句:PostConstruct 和 -Activate 方法不得返回值)。所以这完全是非 CDI 解决方案,也可以在 JEE5 中使用,不是吗?
@MrD 绝对正确!
@MrD 我编辑了初始化方法以返回 void。感谢您的敏锐眼光。
我猜应该删除注释 @PostConstruct 以便您仍然可以使用 CDI
@Armando,我喜欢你的建议。不过我没有尝试过……我想知道 MrD 会怎么说,他一直都是对的。以上是关于如何使用 Weld 注入不可序列化的类(如 java.util.ResourceBundle)的主要内容,如果未能解决你的问题,请参考以下文章
使用 RESTEasy、Weld 和 Wildfly 失败的 @Inject 对象
org.jboss.weld.exceptions.IllegalArgumentException: WELD-001456: 参数resolvedBean 不能为空