@Produces 覆盖成员变量

@Produces over member variable

我试图理解本书 Java EE7 Development with Wildfly 中的一个 JSF 示例,但这里有一些我不理解的东西 - 即使它有效:

豆子:

@Named
@RequestScoped
public class TheatreSetupService {
    ...
    @Produces
    @Named
    private SeatType newSeatType;

    @PostConstruct
    public void initNewSeatType() {
        newSeatType = new SeatType();
    }
    ....
}

XHTML:

<h:form id="reg" role="form">
    <div class="form-group has-feedback #{!desc.valid? 'has-error' : ''}">
        <h:outputLabel for="desc" value="Description"
                       styleClass="control-label"/>
        <h:inputText id="desc" value="#{newSeatType.description}"
                       p:placeholder="Enter a description here" class="form-control"
                       binding="#{desc}"/>
        <span class="#{!desc.valid ? 'glyphicon glyphicon-remove form-control-feedback' : ''}"/>
        <h:message for="desc" errorClass="control-label has-error"/>
    </div>
</h:form>

实体:

@Entity
@Table(name = "seat_type")
public class SeatType implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotNull
    @Size(min = 1, max = 25, message = "Enter a Seat Description (max 25 char)")
    @Pattern(regexp = "[A-Za-z ]*", message = "Description must contain only letters and spaces")
    private String description;

    private SeatPosition position;

    @NotNull
    private Integer price;

    @NotNull
    private Integer quantity;

    @OneToMany(mappedBy = "seatType", fetch = FetchType.EAGER)
    private List<Seat> seats;

    public SeatType() {
        // empty for jpa
    }

    ...

}

我不明白@Produces 对成员变量newSeatType 的影响。创建显然是由 class TheatreSetupService 管理的。对我来说,它看起来就像是成员可用于 jsf 的导出,但 @Named 注释不足以让这个示例工作。谁能解释一下这个小例子中发生了什么?据我所知,这并不经常使用 - 是真的吗?

感谢您的任何提示!

多米尼克

这比 JSF 更特定于 CDI。

@Named 注释是在 CDI 中引入的,旨在成为相同类型的不同 bean 注入的按名称区分的限定符。它还旨在替换以前用 @ManagedBean 注释的 JSF 管理的 bean(如果 运行 在标准 servlet 容器上,如 Tomcat 没有安装 CDI,@ManagedBean 仍然是唯一的解决方案)。因此,一个用 @Named 注释注释的 bean 是一个 CDI 管理的 bean,它可以通过 @Inject 注释注入代码中的任何地方,并且也由 EL 解析器通过其名称解析,如 #{named-bean-name}

要成为支持 CDI 的 bean,class 必须具有默认构造函数,CDI 隐式使用它来创建 class 的实例。在某些情况下(例如,如果 class 没有默认构造函数,或者由于构造函数的可见性受限,或者如果您需要预初始化 bean 实例)创建 bean 实例的唯一方法是生产方法或字段,用于作为 bean 实例创建源。

@Produces-注解的方法或字段旨在充当注入对象的来源。因此,要创建一个像这样的 bean 实例:

public class SeatType {

    public SeatType(Object obj) {}
}

需要提供制作方法:

public class SeatTypeFactory {

    @Produces
    public SeatType createSeatType() {
        return new SeatType(new Object());
    }
}

或生产领域:

public class SeatTypeFactory {

    @Produces
    private SeatType seatType = new SeatType(new Object());
}

到目前为止,您可以通过带有 @Inject 注释的注入在您的代码中使用它,但还不能在 EL 中使用。 EL中解析需要在生产领域或生产方式上指定@Named注解:

@Produces
@Named
private SeatType newSeatType = new SeatType(new Object());

@Produces
@Named("newSeatType")
public SeatType createSeatType() {
    return new SeatType(new Object());
}

在你的情况下,你可以获得几乎相同的效果,只需用 SeatType class 的 @Named 进行注释(不同之处在于 beans 实例,通过 @Produces, 自身不管理,不能包含其他注入点):

@Entity
@Table(name = "seat_type")
@Named("newSeatType")
@RequestScoped
public class SeatType implements Serializable {
    ...
}

在这个例子中,作者可能过度使用了 CDI。您可以像往常一样简单地声明和创建 SeatType bean:

private SeatType newSeatType = new SeatType();

并通过页面中的后台 bean 访问此 bean:

<h:inputText id="desc" value="#{theatreSetupService.newSeatType.description}"

您必须为 newSeatType 属性创建 getter 和 setter。

已编辑:

另一方面,@Produces 注释仅表示您将能够从应用程序的任何其他 CDI bean 中注入此 bean,注入的 bean 将通过 TheatreSetupService CDI bean 的 newSeatType 属性检索。但是如果你想通过 EL 从 JSF 页面之外的任何点注入它,你将需要一个限定符来消除注入点的歧义。

如果您需要从应用程序中的其他几个 bean 注入这个具体 bean,则不会过度使用。 在这种情况下,您必须创建一个限定符来区分您的具体 bean 和全新的 bean。例如:

预选赛:

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, PARAMETER, FIELD})
public @interface TheSpecialSeatType{}

制片人:

@Produces
@Named
@TheSpecialSeatType
private SeatType newSeatType;

帮手 class 使用您的混凝土座椅类型:

public class HelperBean{
    // this bean will be the one created inside the backing bean
    @Inject
    @TheSpecialSeatType
    private SeatType theSeatType;
    ...
}

已添加:

如果您想直接从您的页面访问该属性,就像在示例中那样,那么是的,您将需要 @Named 和 @Produces 注释,如 CDI 文档中所述。 @Produces 通过@Inject 注释将生成的bean 公开给其他bean,并通过EL 公开给JSF 页面。

然后,如果您只想注入到其他 bean 中,请仅使用 @Produces。如果您想在 JSF 页面内注入,请使用 @Produces 和 @Named。