Reputation: 129
I try to understand an JSF Example out of the book Java EE7 Development with Wildfly but here is something I dont understand - even if it works:
Bean:
@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:
@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
}
...
}
I dont understand the effect of @Produces over the member variable newSeatType. Creation is obviousely managed by the class TheatreSetupService. For me it looks like it is just something like an export that the member is available for jsf but the @Named annotation is not enough for this example to work. Can anyone explain me what happens in this little example? As far as I see this is not used very frequently - it that true?
Thank you for any hint!
Dominic
Upvotes: 2
Views: 1114
Reputation: 1420
This is more CDI-specific than JSF-specific.
@Named
annotation was introduced in CDI and is designed to be a by-name-distinguished qualifier for different bean injections of the same types. It is also designed to replace former JSF-managed beans annotated with @ManagedBean
(in case of running on standard servlet container like Tomcat without installed CDI, @ManagedBean
remains as an only solution). Thus, one bean, annotated with @Named
annotation is a CDI-managed bean which can be injected anywhere in code via @Inject
annotation and is also resolved by EL resolvers by its name like #{named-bean-name}
.
To be a CDI-capable bean, one class must have a default constructor, which is implicit used by CDI in order to create an instance of a class. In some cases (for example if class has no default constructor, or because of restricted visibility of constructor, or if you need to preinitialize an instance of a bean) the only way to create a bean instance is a production method or field, which are used to be a bean instance creation source.
@Produces
-annotated methods or fields are designed to act as a source of objects for injection. Thus, to create an instance of a bean like:
public class SeatType {
public SeatType(Object obj) {}
}
we need to provide a production method:
public class SeatTypeFactory {
@Produces
public SeatType createSeatType() {
return new SeatType(new Object());
}
}
or a production field:
public class SeatTypeFactory {
@Produces
private SeatType seatType = new SeatType(new Object());
}
So far, you can use it in your code via injection with @Inject
annotation, but not yet in EL. To be resolved in EL, you need to specify an @Named
annotation on a production field or production method:
@Produces
@Named
private SeatType newSeatType = new SeatType(new Object());
@Produces
@Named("newSeatType")
public SeatType createSeatType() {
return new SeatType(new Object());
}
In your case, you could achieve almost the same, just by annotating with @Named
of your SeatType
class (the difference is that beans instances, created via @Produces
, are not managed itself and cannot contain other injection points):
@Entity
@Table(name = "seat_type")
@Named("newSeatType")
@RequestScoped
public class SeatType implements Serializable {
...
}
Upvotes: 1
Reputation: 1255
In this example the author perhaps is overusing CDI. You can simply declare and create the SeatType bean as usual:
private SeatType newSeatType = new SeatType();
and access this bean through the backging bean in your page:
<h:inputText id="desc" value="#{theatreSetupService.newSeatType.description}"
You will have to create getter and setter for newSeatType attribute.
EDITED:
On the other hand, @Produces annotation only indicates that you will be able to inject this bean from any other CDI bean of your application, and the injected bean will be retrieve through the newSeatType attribute of the TheatreSetupService CDI bean. But if you want to inject it from any point apart of the JSF page via EL you will need a qualyfier to disambiguate the injection point.
There would not be overusing if you needed to inject this concrete bean from several other beans in the application. In this case you would have to create a qualifier to distinguish your concrete bean from a brand new bean. For instance:
The qualifier:
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, PARAMETER, FIELD})
public @interface TheSpecialSeatType{}
The producer:
@Produces
@Named
@TheSpecialSeatType
private SeatType newSeatType;
A helper class using your concrete seat type:
public class HelperBean{
// this bean will be the one created inside the backing bean
@Inject
@TheSpecialSeatType
private SeatType theSeatType;
...
}
ADDED:
If you want to access the attribute directly from your page, like in the example, then yes, you will need both @Named and @Produces annotation as said in the CDI documentation. @Produces exposes the produced bean to others beans via @Inject annotation and to JSF pages via EL.
Then, if you only want to inject inside other beans use @Produces only. If you want to inject inside JSF pages use @Produces and @Named.
Upvotes: 1