【问题标题】:Referencing CDI producer method result in h:selectOneMenu引用 CDI 生产者方法导致 h:selectOneMenu
【发布时间】:2011-11-20 11:25:05
【问题描述】:

我有一个命名的会话范围 bean CustomerRegistration,它有一个命名的生产者方法 getNewCustomer,它返回一个 Customer 对象。还有CustomerListProducer 类,它从数据库中生成所有客户作为列表。在 selectCustomer.xhtml 页面上,用户可以选择其中一位客户并将选择提交给应用程序,然后应用程序简单地打印出所选客户的姓氏。

现在,这仅在我通过 #{customerRegistration.newCustomer} 在 facelets 页面上引用所选客户时才有效。当我简单地使用#{newCustomer} 时,每当我提交表单时,姓氏的输出就是null

这里发生了什么?这是根据 JSR-299 规范的第 7.1 章 对 bean 实例化的限制 的预期行为吗?

上面写着:

... 但是,如果应用程序直接实例化一个 bean 类, 而不是让容器执行实例化,结果 实例不由容器管理,也不是上下文 第 6.5.2 节“bean 的上下文实例”定义的实例。 此外,第 2.1 节“功能”中列出的功能 由容器提供给 bean”将不可用 具体事例。在已部署的应用程序中,它是容器 负责实例化 bean 并初始化它们的 依赖关系。 ...

代码如下:

客户.java:

@javax.persistence.Entity
@Veto
public class Customer implements Serializable, Entity {
    private static final long serialVersionUID = 122193054725297662L;
    @Column(name = "first_name")
    private String firstName;
    @Column(name = "last_name")
    private String lastName;
    @Id
    @GeneratedValue()
    private Long id;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public String toString() {
        return firstName + ", " + lastName;
    }

    @Override
    public Long getId() {
        return this.id;
    }
}

CustomerListProducer.java:

@SessionScoped
public class CustomerListProducer implements Serializable {

    @Inject
    private EntityManager em;

    private List<Customer> customers;

    @Inject
    @Category("helloworld_as7")
    Logger log;

    // @Named provides access the return value via the EL variable name
    // "members" in the UI (e.g.,
    // Facelets or JSP view)
    @Produces
    @Named
    public List<Customer> getCustomers() {
        return customers;
    }

    public void onCustomerListChanged(
            @Observes(notifyObserver = Reception.IF_EXISTS) final Customer customer) {
//      retrieveAllCustomersOrderedByName();
        log.info(customer.toString());
    }

    @PostConstruct
    public void retrieveAllCustomersOrderedByName() {
        CriteriaBuilder cb = em.getCriteriaBuilder();
        CriteriaQuery<Customer> criteria = cb.createQuery(Customer.class);
        Root<Customer> customer = criteria.from(Customer.class);
        // Swap criteria statements if you would like to try out type-safe
        // criteria queries, a new
        // feature in JPA 2.0
        // criteria.select(member).orderBy(cb.asc(member.get(Member_.name)));
        criteria.select(customer).orderBy(cb.asc(customer.get("lastName")));
        customers = em.createQuery(criteria).getResultList();
    }
}

CustomerRegistration.java:

@Named
@SessionScoped
public class CustomerRegistration implements Serializable {

    @Inject
    @Category("helloworld_as7")
    private Logger log;

    private Customer newCustomer;

    @Produces
    @Named
    public Customer getNewCustomer() {
        return newCustomer;
    }

    public void selected() {
        log.info("Customer " + newCustomer.getLastName() + " ausgewählt.");
    }

    @PostConstruct
    public void initNewCustomer() {
        newCustomer = new Customer();
    }

    public void setNewCustomer(Customer newCustomer) {
        this.newCustomer = newCustomer;
    }

}

不工作 selectCustomer.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!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:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <title>Auswahl</title>
</h:head>
<h:body>
    <h:form>
        <h:selectOneMenu value="#{newCustomer}" converter="customerConverter">
            <f:selectItems value="#{customers}" var="current"
                itemLabel="#{current.firstName}, #{current.lastName}" />
        </h:selectOneMenu>
        <h:panelGroup id="auswahl">
            <h:outputText value="#{newCustomer.lastName}" />
        </h:panelGroup>
        <h:commandButton value="Klick"
            action="#{customerRegistration.selected}" />
    </h:form>
</h:body>
</html>

工作 selectCustomer.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!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:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
    <title>Auswahl</title>
</h:head>
<h:body>
    <h:form>
        <h:selectOneMenu value="#{customerRegistration.newCustomer}" converter="customerConverter">
            <f:selectItems value="#{customers}" var="current"
                itemLabel="#{current.firstName}, #{current.lastName}" />
        </h:selectOneMenu>
        <h:panelGroup id="auswahl">
            <h:outputText value="#{newCustomer.lastName}" />
        </h:panelGroup>
        <h:commandButton value="Klick"
            action="#{customerRegistration.selected}" />
    </h:form>
</h:body>
</html>

CustomerConverter.java:

@SessionScoped
@FacesConverter("customerConverter")
public class CustomerConverter implements Converter, Serializable {
    private static final long serialVersionUID = -6093400626095413322L;

    @Inject
    EntityManager entityManager;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component,
            String value) {
        Long id = Long.valueOf(value);
        return entityManager.find(Customer.class, id);
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component,
            Object value) {
        return ((Customer) value).getId().toString();
    }

}

【问题讨论】:

    标签: jsf jsf-2 cdi jboss-weld


    【解决方案1】:

    想想@Producers 是如何注册的。在部署期间,容器会扫描您的类中的注释,并且您在 @SessionScoped @Named bean 中声明了 @Producer 方法这一事实并不意味着您将拥有与该 bean 实例一样多的生产者,也不是表示容器将从您希望调用它的实例调用生产者。

    这里发生的情况是 - 您的生产者方法总是返回相同的 Customer 实例,该实例是在部署期间在您的 @PostConstruct 方法中创建的,即在 @Producer 注册期间。

    这是预期的行为。

    您似乎想要在每个会话中为您提供一个新的Customer 实体。在这种情况下,正确的做法是:

    public class CustomerProducer {
    
        @Produces @Named @SessionScoped
        public Customer getNewCustomer(@New Customer customer) {
            // do some custom init if need be
            return customer;
        }
    
    }
    

    然后从会话范围的 bean 中删除 @Producer 相关的注释。现在您可以使用 `#{newCustomer},它总是会为每个会话提供一个新的容器托管实例。

    【讨论】:

      猜你喜欢
      • 2015-10-01
      • 2012-06-10
      • 1970-01-01
      • 2018-08-18
      • 2017-09-25
      • 1970-01-01
      • 2023-03-29
      • 2015-04-08
      • 1970-01-01
      相关资源
      最近更新 更多