如何使用 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.PersonH​​ome 是有状态会话 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 不能为空

Google Guice 与 JSR-299 CDI / Weld

怎样对带有不可序列化属性的Java对象进行序列化

从 appdomain 中解包不可序列化的类

如何在 WildFly 上禁用 WELD