【问题标题】:Inject a @Named @ViewScoped into a @SessionScoped将 @Names @ViewScoped 注入 @SessionScoped
【发布时间】:2018-08-05 05:24:45
【问题描述】:

我有两个使用@Named 的bean,一个使用@SessionScoped,另一个使用@ViewScoped。我可以将@ViewScoped bean 注入@SessionScoped 并尝试做相反的事情,我几乎可以工作,但我没有相同的实例。

当我在 viewScoped Bean 的 @PostContruct 方法中打印 this.hashcode() 并将其与 sessionScoped Bean 中注入的比较时,我可以看到它。

所以我找到了一个解决方案,但我不知道这是否是一个好习惯:在注入 SessionScoped bean 后,在 ViewScoped bean 的 @PostContruct 方法中,我通过 à setter 将 ViewScoped 发送到 SessionScoped。

如果我充分理解这些对象与用户相关联,所以不会造成任何麻烦,对吗?

@Named
@ViewScoped
public class ViewScopedBean {

    @Inject
    protected SessionScopedBean sessionScopedBean;

    @PostContruct
    public void init(){

        sessionScopedBean.setViewScopedBean(this);
    }
}

@Named
@SessionScoped
public class SessionScopedBean {

    protected ViewScopedBean viewScopedBean ;

    public void setViewScopedBean(ViewScopedBean viewScopedBean){

        this.viewScopedBean = viewScopedBean;
    }
}

【问题讨论】:

  • 这里需要注意两点。首先,你得到的那两个视图范围的bean实际上是不同的吗?请记住,它们(很可能)是代理,因此请使用它们的内部状态而不是 hashCode(可以在代理级别调用)来验证它。其次 - 你确定你足够了解ViewScoped 生命周期吗?例如。您得到两个不同的实例可能是合乎逻辑的,因为它绑定到 JSF 视图。另见this answer
  • 谢谢你的回答,你是对的!我检查了一下,它是嵌入在代理中的同一个对象。如果我在 Injected ViewScopedBean 上调用哈希码,我会看到代理的哈希码,但如果我调用嵌入式 bean 的方法(将哈希码打印到),我可以看到它是相同的。关于生命周期,我认为是这样,但我不知道所有细节(我应该 ^^),我使用 ViewScoped Bean 来保持 primefaces 树组件的状态,可以使用延迟加载扩展节点。
  • 使用旧的 ManagedBean 系统,我无法使用 @ManagedProperty 注释将 viewScopedBean 注入到 sessionScopedBean 中。但在这里我很想知道它是如何工作的。我想任何时候 ViewScopedBean 更改它都会重新注入到 SessionScopedBean 中,对吗?
  • 我不熟悉视图范围的实现细节,但从 CDI 的角度来看(视图范围来自 JSF),您应该始终在给定的上下文和线程中注入当前活动的(如果有的话)视图范围 bean .该底层视图范围 bean 的创建/替换是由 JSF impl 强制执行的,并且应该在创建/退出视图时发生。
  • 我已将评论提取到答案中,以便可以解决这个 SO 问题。

标签: jsf cdi named-scope


【解决方案1】:

将我在 cmets 中写的内容提取为答案:

这里的问题是 CDI/Weld 不直接 @Inject 上下文实例,而是移交代理实例(对于 @NormalScoped bean!),然后委托给一个基础 bean 实例;在这种情况下@ViewScoped bean。

在问题中假设hashCode() 可用于注入的对象以验证它们是否相同。然而,这并不一定是真的,因为 CDI 可以选择为每个注入点提供不同的代理,并且 hashCode() 将在代理对象上调用,而不是在底层 bean 实例上调用 - 因此存在差异。

方法是检查 bean 的内部状态,这被证明是相等的,因此表明两个注入点都注入了相同的 @viewScoped bean 实例。

【讨论】:

  • 如果在代理对象上实现了hashCode()方法,代理对象会调用代理对象的hashCode()方法...equals()也是如此
  • hashCode(以及扩展名equals)很可能在那里实现(您可以在焊接和检查中轻松转储代理的字节码),这就是发生这种行为的原因。不同的代理被传送到两个不同的 IP。
【解决方案2】:

我看到两个问题:

  1. 你的主要问题是你有循环依赖。尽量避免这种情况。 ViewScopedBean 不能依赖于 SessionScopedBean,反之亦然,因为 CDI 不知道首先创建哪个。 (这就是为什么您必须在 @PostConstruct 方法中手动设置依赖项的原因。)
  2. 您不能通过比较它们的引用来比较 Bean,因为它们会被序列化/非序列化,因此不是同一个对象。这就是为什么您应该使用equals() 比较它们并实现hashCode() 方法。

【讨论】:

  • CDI 默认可以处理一定数量的循环依赖,这要归功于它的惰性启动(至少在 Weld 中)和代理。但据我了解,OP 引入这种依赖关系只是为了验证注入行为?但你说得有道理。至于 2. - 再次,这是 Weld impl 详细信息,但您可以看到这些是如何为客户端代理创建的 here。阅读 javadoc 以了解其工作原理,您的答案具有误导性且不成立。
  • @thobens 谢谢,我不确定,但我认为可以将 sessionScopedBean 注入到 viewScopedBean 中,因为 sessionScopedBean 是首先创建的,因为它与用户会话相关联,然后必须创建 viewScopedBean当调用特定视图时。它似乎很好地处理了循环依赖。
  • 无论如何,如果我想手动将其放入@PostConstruct 并避免循环依赖,我至少必须注入其中一个:p。并感谢有关序列化的信息,我忘记了!
猜你喜欢
  • 2014-04-16
  • 1970-01-01
  • 2012-11-02
  • 2014-10-20
  • 2016-08-02
  • 1970-01-01
  • 2015-04-15
  • 1970-01-01
  • 2012-02-07
相关资源
最近更新 更多