【问题标题】:How to replace @ManagedBean / @ViewScope by CDI in JSF 2.0/2.1如何在 JSF 2.0/2.1 中用 CDI 替换 @ManagedBean / @ViewScope
【发布时间】:2015-11-12 00:28:10
【问题描述】:

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

声明为

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

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

@Named
@ConversationScoped

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

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

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

【问题讨论】:

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

标签: jsf jsf-2 cdi view-scope


【解决方案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";
    }

}

另见:

【讨论】:

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

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

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

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

【讨论】:

    【解决方案3】:

    将对话注入到您的 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";
        }
    
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

        你可以使用:

        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
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-16
        • 1970-01-01
        • 2013-02-26
        • 1970-01-01
        • 2020-03-08
        相关资源
        最近更新 更多