当前位置: 首页 > 知识库问答 >
问题:

从RequestMap中提取RequestScope CDIBean

鲜于宜修
2023-03-14

我目前正在将Ed Burns的JSF 2.0教科书中的虚拟培训师示例应用程序从JSF托管bean转换为CDI。到目前为止,我遇到的大多数问题都与作用域和忘记正确注入有关,但现在我正在努力克服最近的一个障碍,即从RequestMap中提取CDIBean(实际上是一个实体类)。从目前为止我所能确定的情况来看,似乎可以通过使用样板文件非常简单地提取请求范围的托管Bean。映射实现提供的get(String managedbeanname)方法。然而,使用CDI,bean在CreationContextImp实例中被Weld包裹,我无法提取我真正想要的对象,即使我已经确认它存在于RequestMap中。我可以从RequestMap访问代理对象,但调用后会恢复为null。get(“user”),我怀疑它会有多大用处,因为代理中的字段都为空。

我发现了一篇来自BalusC的帖子,其中讨论了如何使用筛选器类访问SessionScope中的CDIBean(如何从筛选器中获取SessionScope的CDIBean?)这似乎有点牵扯其中——有更简单的解决方案吗?我也很清楚,我可能会搞砸托管Bean与CDI策略的范围界定/混合,所以请随意告诉我。。。我也有点不确定是否以这种方式直接使用实体Bean,而不是使用外观。这会不会给我带来麻烦?

环境:JEE7、Glassfish 4、Netbeans 7.4、Maven EE Web原型与托管bean的使用相关的原始代码已被注释掉。

抽象支持bean类:

@RequestScoped
public abstract class AbstractBacking implements Serializable {

    //@ManagedProperty(value="#{facesContext}")
    private FacesContext facesContext;

    //@ManagedProperty(value="#{requestScope}")
    private Map<String, Object> requestMap;

    //@ManagedProperty(value="#{sessionScope}")
    private Map<String, Object> sessionMap;

    @PostConstruct
    public void init() {
        this.facesContext = FacesContext.getCurrentInstance();
        this.sessionMap = FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
        this.requestMap = FacesContext.getCurrentInstance().getExternalContext().getRequestMap();
    }

注册页面备份bean:

@Named
@RequestScoped
public class RegisterBacking extends AbstractBacking implements Serializable {

    private Object password1;

    @Inject
    private User newUser;

    public String registerUser() {
        String result = null;
        User newUser = (User) getRequestMap().get("user");
        // set the password into the user, because we know the validator was
        // successful if we reached here.
        newUser.setPassword((String) getRequestMap().get("password1"));
        try {
            UserRegistry.getCurrentInstance().addUser(newUser);
            // Put the current user in the session
            setCurrentUser(newUser);
            // redirect to the main page
            result = "/user/allEvents?faces-redirect=true";
        } catch (EntityAccessorException ex) {
            getFacesContext().addMessage(null,
                    new FacesMessage("Error when adding user"
                            + ((null != newUser) ? " " + newUser.toString() : "") + "."));

        }

        return result;

    }

用户实体bean:

@Entity
@Named
@Table(name = "Users")
@RequestScoped
@NamedQueries({
    @NamedQuery(name = "user.getAll", query = "select u from User as u"), // @NamedQuery(name = "user.getTrainers", query = "select u from User as u where u.trainer = TRUE"),
// @NamedQuery(name = "user.getUsersForTrainerId", query = "select u from User as u where u.personalTrainerId = :theId")
})

public class User extends AbstractEntity implements Serializable {

    protected String firstName;
    protected String lastName;
    @Temporal(TemporalType.DATE)
    protected Date dob;
    protected String sex;
    protected String email;
    private String serviceLevel = "medium";
    @Column(name = "userid", nullable = false)
    private String userid;
    private String password;
    private boolean trainer;
    private List<Long> subscribedEventIds;
    private Long personalTrainerId;

    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<TrainingSession> sessions;

    private boolean sessionsInitialized = false;

    public User() {
        this.init();
    }

    public User(String firstName, String lastName,
            String sex, Date dob, String email, String serviceLevel,
            String userid, String password, boolean isTrainer) {
        this.init();
        this.setFirstName(firstName);
        this.setLastName(lastName);
        this.setSex(sex);
        this.setDob(dob);
        this.setEmail(email);
        this.setServiceLevel(serviceLevel);
        this.setUserid(userid);
        this.setPassword(password);
        this.setTrainer(isTrainer);
    }
.....
Getters/setters/etc
.....

注册页面:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">
<body>
<ui:composition template="template.xhtml">
    <ui:define name="content">
<h:form prependId="false">
    <h:panelGrid columns="3">

        <h:outputLabel for="fname" value="First Name:" />
        <h:inputText label="First Name"
                     id="fname" value="#{user.firstName}"
                     required="true"/>
        <h:message for="fname" />

        <h:outputLabel for="lname" value="Last Name:" />
        <h:inputText label="Last Name"
                     id="lname" value="#{user.lastName}"
                     required="true"/>
        <h:message for="lname" />

        <h:outputLabel for="sex" value="Sex:" />
        <h:selectOneRadio label="Sex"
                          id="sex" value="#{user.sex}" required="true">
          <f:selectItem itemLabel="Male" itemValue="male" />
          <f:selectItem itemLabel="Female" itemValue="female" />
        </h:selectOneRadio>
        <h:message for="sex" />

        <h:outputLabel for="dob" value="Date of Birth:" />
        <h:panelGroup>
            <h:inputText label="Date of Birth"
                     id="dob" value="#{user.dob}" required="true">
                <f:convertDateTime pattern="MM-dd-yy" />
            </h:inputText> (mm-dd-yy)
        </h:panelGroup>
        <h:message for="dob" />

        <h:outputLabel for="email" value="Email Address:" />
        <h:inputText label="Email Address"
                     id="email" value="#{user.email}" required="true" />
        <h:message for="email" />

        <h:outputLabel for="slevel" value="Service Level:" />
        <h:selectOneMenu label="Service Level" id="slevel"
                         value="#{user.serviceLevel}">
          <f:selectItem itemLabel="Medium" itemValue="medium" />
          <f:selectItem itemLabel="Basic" itemValue="basic" />
          <f:selectItem itemLabel="Premium" itemValue="premium" />
        </h:selectOneMenu>
        <h:message for="slevel" />

        <h:outputLabel for="userid" value="Userid:" />
        <h:inputText required="true" id="userid" value="#{user.userid}" />
        <h:message for="userid" />

        <h:outputLabel for="password" value="Password:" />
        <h:inputSecret required="true" id="password" 
                       validator="#{registerBacking.validatePassword1}"
                       value="#{requestScope.password1}" />
        <h:message for="password" />

        <h:outputLabel for="password2" value="Retype Password:" />
        <h:inputSecret required="true" id="password2" value="#{requestScope.password2}"
                       validator="#{registerBacking.validatePassword2}" />
        <h:message for="password2" />


    </h:panelGrid>

    <p><h:commandButton value="Register" 
                     action="#{registerBacking.registerUser}" /></p>
</h:form>

    </ui:define>
</ui:composition>
</body>
</html>

共有1个答案

杨海
2023-03-14

事实上,您似乎混合了cdibean和JSF bean,而且说实话,您正在翻译的示例似乎很奇怪,我会将其一起扔出窗口。

因为,JSF也有它自己的依赖注入,除非你有一个非常特定的用例(比如ServletFilter),否则你不会真正从作用域映射中提取东西。

要澄清CDI,请使用http://en.wikipedia.org/wiki/Inversion_of_control原则和自己“提取”东西是一种反模式。

此外,CDI bean由CDI容器创建和管理(几乎所有情况下都是焊接或OWB),您根本无法从外部上下文检索CDI bean,因为它是JSF的上下文,是完全独立的东西。

因此,JSF创建的实例不能在CDIBean中注入,反之亦然。不管怎样,正如我前面所说的,自己提取东西是一种不好的做法。请参见此列表中的#10:http://zeroturnaround.com/rebellabs/watch-out-for-these-10-common-pitfalls-of-experienced-java-developers-architects/

因此,您只需使用@Inject(可能带有限定符)即可。

解决方法对于Inject无法工作的特殊情况:

  • CDI上下文在当前线程中不可用
  • 当前实例不是CDI管理的

第一种情况发生时,你有一个ThreadLocal-例如QuartzJobs。EJB容器可能提供了获取上下文线程的方法,但是如果您使用的是普通的servlet容器(tomcat),或者线程是在没有CDI上下文的情况下生成的,无论出于什么原因,您都必须自己附加这些信息。为此,请使用Deltaspike CDI Control。

http://deltaspike.apache.org/container-control.html

该示例未更新,今天您应该使用DependentProvider来获取ContextControl。

我博客上的例子:EE/中的http://www.kildeen.com/blog/post/2013-10-11/Batch工作

第二种情况是,当ServletContainer创建了实例,而您在一个普通容器(例如Tomcat)上时,JSF创建了实例。

那么,注射就不起作用了。作为解决方法,请使用Deltaspike的BeanProvider。http://deltaspike.apache.org/core.html它可以在当前实例中强制注入,更有用的是,它可以为您提取bean。如果您这样做,并且仍然受到第一种情况的影响,您将得到一个异常,可能是ContextNotActiveException

为了正确地开始使用JSF和CDI,我建议使用TOME。这是一个apache项目,因此是开源的。邮件列表和irc频道非常活跃,而且还在上升,即使现在也非常棒。

当然,这是我的观点,其他人更喜欢其他解决方案,如WildFly、用Tomcat/Jetty自己建造、Glassfish等。

现在,JSF的所有常规规则都适用(对于getter/setter等必须使用约定)。要将bean公开给EL表达式(JSF使用的),必须将其标记为@Named。如果类名为myBean,则默认名称为myBean

现在是示波器。始终对应用程序中的每个bean使用@RequestScope。当你遇到麻烦时,因为这个范围很短,你应该考虑它应该是多长时间,如果你想要保留的数据对于其他bean来说是有趣的,并且如果你希望它在所有浏览器标签中都可用。

如果它是用户信息,那么对许多bean来说很有可能是有趣的。因此,我们创建了一个名为WebUser的新类。用户可能希望他的信息在整个会话期间都被保留(您可能还需要使用该对象跟踪他)。因此,我们使用来自正确包的@SessionScoped,必须是importjavax。企业上下文已处理会话。但是突然之间,有些逻辑不应该在选项卡之间共享,所以您需要一个更细粒度的范围。CDI附带了@ViewScoped(CDI1.1,JSF2.2)和@ConversationScoped,但很可能您迟早会想要Myfaces CODI的作用域。现在,大部分CODI已经在deltaspike,但不是scopes。但是,它们已被拆分,此处说明了如何导入它们:http://os890.blogspot.fr/2013/07/add-on-codi-scopes-for-deltaspike.html你迟早会在德尔塔斯派克看到他们。

因此,现在您可以使用许多不同的作用域,并且您可以明智地选择它。只能从数据库中读取一次的内容可以使用@ApplicationScoped存储。

例如,您可能希望从数据库读取系统设置,并将它们存储在SettingManager中,SettingManager是一个用@ApplicationScoped注释的CDIBean。

WelcomeBean@RequestScoped@Model并负责登录和帐户创建。要了解是否可以创建新帐户,可能如下所示:

@ViewScoped
@Named
public class WelcomeBean {

    @Inject
    private SettingManager settingManager;


    private boolean allowCreateAccount;


    public boolean isAllowCreateAccount() {
        return allowCreateAccount;
    }


   // login and create account here

    @PostConstruct
    private void init() {
        allowCreateAccount = settingManager.getBooleanSetting("registrationOpen");
    }
}

Facelet创建帐户按钮如下所示:

<h:commandButton action="#{welcomeBean.createAccount}" value="login" disabled="#{welcomeBean.allowCreateAccount}"/>

现在,当用户执行登录时,您可能希望将此作为事件发出信号。阅读CDI事件。实际上,最高级的示例与这个简单的示例几乎没有什么不同。

 类似资料:
  • 使用OpenXML(C#)解析*. docx文档有一个问题。 下面是我的步骤: 1。加载*。docx文档 2。接收段落列表 3。在每个段落中查找文本、图像和表格元素 4。为每个文本和图像元素创建html标记 5。将输出另存为*。html文件 我已经了解了如何在文档中定位图像文件并将其解压缩。现在有一个步骤要做——找到表格在文本(段落)中的位置。 如果有人知道如何在*中定位表。docx文档使用Ope

  • Q非常业余的程序员在这里,寻求你的帮助。 我必须经常编辑这样的xml文件 使用一个相当复杂的正则表达式搜索和替换过程,我只能提取标记属性的值。(这就是我所关心的)。 但是这很耗时,而且在Python中必须有非常简单的方法来查找属性标记="SOME_TEXT"部分并将所有值放入一个数组中,然后打印出该数组(到文件中)。但是我无法弄清楚:( 我正在寻找一种不包括导入任何类型的XML库的方法,因为我想让

  • 问题内容: AJAX调用返回的响应文本包括JSON字符串。我需要: 提取JSON字符串 修改它 然后重新插入以更新原始字符串 我不太担心步骤2和3,但是我不知道如何执行步骤1。我当时在考虑使用正则表达式,但是我不知道该怎么做,因为我的JSON可能具有嵌套对象的多个级别或数组。 问题答案: 您不能使用正则表达式从任意文本中提取JSON。由于正则表达式通常不够强大,无法验证JSON(除非可以使用PCR

  • 我有以下字符串 从上面,我需要提取下面的文字 第一个数字和文本之间总是有一个空格,所以2129和This is page1之间有一个空格。有时第一个数字被省略,就像2129不见了。文本和下一个数字之间总是有一个空格,所以在This is a Page1和6754001之间有一个空格,有时可能有两个空格。我只需要提取这些线这些线总是从空格开始,所以它可以 它们的后面总是有一个空格,有时是一个空格,有

  • 我想为我的搜索引擎从数据库中提取一个基本的同义词列表。这包括通常拼写的名字,如Shaun vs.Shawn,Muhammad的不同变体,命名实体的首字母缩写,如United Nations(UN)或SARS(Severe acute respiratory syndrome)。 在提取之后,这个同义词列表将被放置在服务器中,并以这样的方式存储--相关术语/同义词的字符串。 示例 我使用了jaws

  • 我有一个类游戏面板方法更新()。如何将该方法提取到单独的文件(类)? 我尝试进行类更新: