如何在 JSF 2.0/2.1 中用 CDI 替换 @ManagedBean / @ViewScope

Posted

技术标签:

【中文标题】如何在 JSF 2.0/2.1 中用 CDI 替换 @ManagedBean / @ViewScope【英文标题】:How to replace @ManagedBean / @ViewScope by CDI in JSF 2.0/2.1 【发布时间】:2013-01-01 07:18:10 【问题描述】:

我目前正在使用 RichFaces 评估 Java EE 6 / JSF 2.1。

声明为

的 bean
@ManagedBean
@ViewScoped
    获取一个 ID 集(准备例如删除操作)。 通过 JSF 会显示一个确认弹出窗口。 如果用户确认,则调用 delete 方法并删除在步骤 1 中为其存储 ID 的行。

由于 CDI bean 没有 ViewScope,我尝试将 bean 声明为:

@Named
@ConversationScoped

现在第 3 步中的处理失败。因为在第 1 步中设置的值(已检查)不再可用。

我必须使用Conversation.begin()Conversation.end() 方法吗?

如果是这样,哪里是调用它们的好地方?

【问题讨论】:

供将来参考:在 JSF 2.2 中,核心 JSF 中有一个 CDI 兼容的视图范围。 【参考方案1】:

如果您可以升级到 JSF 2.2,请立即升级。它为 CDI 提供原生 @ViewScoped 注释。

import javax.faces.view.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class Bean implements Serializable 
    // ...

或者,安装 OmniFaces,它会带来自己的 CDI 兼容 @ViewScoped,包括一个正常工作的 @PreDestroy(在 JSF @ViewScoped 上已损坏)。

import javax.inject.Named;
import org.omnifaces.cdi.ViewScoped;

@Named
@ViewScoped
public class Bean implements Serializable 
    // ...

另一种选择是安装 MyFaces CODI,它将 JSF 2.0/2.1 @ViewScoped 透明地桥接到 CDI。这只会向 URL 添加一个自动生成的请求参数(就像 @ConversationScoped 一样)。

import javax.faces.bean.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class Bean implements Serializable 
    // ...

如果你真的需要使用@ConversationScoped,那么你确实需要手动开始和结束它。您需要在对话的最后一步中 @InjectConversation 并在 @PostConstructend() 中调用 begin(),通常是重定向到新视图的操作方法。

import javax.enterprise.context.Conversation;
import javax.enterprise.context.ConversationScoped;
import javax.inject.Named;

@Named
@ConversationScoped
public class Bean implements Serializable 

    @Inject
    private Conversation conversation;

    // ...

    @PostConstruct
    public void init() 
        conversation.begin();
    

    public String submit() 
        // ...

        conversation.end();
        return "some.xhtml?faces-redirect=true";
    


另见:

How to choose the right bean scope?

【讨论】:

感谢您的快速回复。尚未结束的对话会发生什么,例如用户只是导航到另一个页面? 它在会话结束时过期(顺便说一下,就像@ViewScoped bean)。请注意,对话范围由特定的请求参数标识(在 Weld 中,即 cid),因此它的行为不像会话范围的 bean。如果您使用该对话框创建新请求,即使旧对话尚未结束,也会开始新对话。【参考方案2】:

有一个项目包含对 Java EE 堆栈功能的扩展:DeltaSpike。它是 Seam 3、Apache CODI 的整合。最重要的是,它将@ViewScoped 包含在 CDI 中。这是一篇老文章,现在已经到了1.3.0版本

【讨论】:

【参考方案3】:

你可以使用:

import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;

@Named
@ViewScoped
public class PageController implements Serializable 

    private String value;

    public void setValue(String value) 
    this.value = value;
    

    public String getValue() 
    return value;
    

    public void execute() 
    setValue("value");
    

    @PostConstruct
    public void init() 
    System.out.println("postcontructor");
    


【讨论】:

你**不能** jsf 2.1,这就是问题所在。因此,请增强您的答案,使其包含 JSF 2.2【参考方案4】:

我认为您可以从 CDI 扩展中受益,创建自己的范围,这样您就可以实现上下文并使用 @NormalScope

CDI 在每次 bean 调用后触发一个事件 AfterBeanDiscovery 您可以使用 CDI 扩展来 @Observes 此事件并添加您的上下文实现 在您的作用域实现中,您可以:
    使用 ContextualFacesContext ViewRoot Map 获取您的 bean 并在每次 ajax 回调后返回它 如果找不到第一步中的 bean 名称,请使用 CreationalContextFacesContext ViewRoot Map 中创建它

更深入的解释,我推荐这个链接:http://www.verborgh.be/articles/2010/01/06/porting-the-viewscoped-jsf-annotation-to-cdi/

【讨论】:

【参考方案5】:

将对话注入到您的 bean 中,如果对话是暂时的,则在 @PostConstructor 方法中启动对话。

删除记录后,结束对话并导航到目标页面。开始对话时。这是一个例子

public class BaseWebBean implements Serializable 

private final static Logger logger = LoggerFactory.getLogger(BaseWebBean.class);
@Inject
protected Conversation conversation;

@PostConstruct
protected void initBean()


public void continueOrInitConversation() 
        if (conversation.isTransient()) 
            conversation.begin();
            logger.trace("conversation with id  has started by .", conversation.getId(), getClass().getName());
        
    

public void endConversationIfContinuing() 
        if (!conversation.isTransient()) 
            logger.trace("conversation with id  has ended by .", conversation.getId(), getClass().getName());
            conversation.end();
        

@ConversationScoped
@Named
public class yourBean extends BaseWebBean implements Serializable 
    @PostConstruct
    public void initBean() 
        super.initBean();
        continueOrInitConversation();
    

    public String deleteRow(Row row)
    
        /*delete your row here*/
        endConversationIfContinuing();
        return "yourDestinationPageAfter removal";
    


【讨论】:

以上是关于如何在 JSF 2.0/2.1 中用 CDI 替换 @ManagedBean / @ViewScope的主要内容,如果未能解决你的问题,请参考以下文章

无法将 JSF + CDI 项目从 Tomcat 迁移到 Wildfly

是否在会话范围的 JSF 支持 bean 中观察到 CDI 事件

JSF、CDI 和 EJB 容器:应该使用它们的哪种组合?

CDI 托管 bean 和 JSF 托管 bean 可以相互通信吗?

在会话作用域的JSF支持bean中观察CDI事件

JSF-2.3 找不到我的 @Named CDI-1.2 托管 bean