One of the most important and useful features of JSF 2.0 consists in annotations. Based on them, JSF 2.0 provides an easy way to accomplish important tasks. In this recipe, we will present the most commonly used annotations and we will see what they can do for us.
If you are a JSF 1.2 fan, then you are familiar with the faces-config.xml
configuration file. Starting with JSF 2.0, the content of this descriptor can be partially (sometimes totally) replaced with annotations.
The most common case is represented by the managed bean, which can be annotated as shown, instead of placing a specific declaration in faces-config.xml
:
import javax.faces.bean.ManagedBean; @ManagedBean public class MyBean { … }
In the previous example, the bean is referenced as myBean
, but you may specify another name, as shown next:
@ManagedBean(name="coolBean")
And what is a managed bean without a context (a scope)? JSF 2.0 supports an entire list of scope annotation, as shown next:
Annotation |
Annotation Class |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
In addition, we can annotate a managed bean's properties using the @ManagedProperty
annotation. The presence of this annotation on a field of a class annotated with @ManagedBean
instructs the system to inject a value into this property:
@ManagedProperty("fooval") private String foo; @ManagedProperty("#{fooval}") private String foo;
Going further, you can react to the creation and the destruction of a managed bean, as shown next:
public class MyBean { @PostConstruct public void postCreate(){ … } @PreDestroy public void preDestroy(){ … }
If you use JSF inside of a JEE container you can inject resources, session, message-driven beans, and web services into your managed beans. Something like the following is perfectly legal:
@ManagedBean @SessionScoped public class MyBean implements Serializable { @EJB private Facade facade; …
JSF 2.0 specification has added the @ResourceDependency
annotation to allow component authors to declare the resources the component will need. For example:
@ResourceDependency(name="my.css",library="libus") public class MyComponent extends UIComponentBase { … }
You may use more than one @ResourceDependency
using the @ResourceDependencies
annotation, as the following:
@ResourceDependencies({ @ResourceDependency(name="my.css",library="libus"), @ResourceDependency(name="my.js",library="libus",target="head") }) public class MyComponent extends UIComponentBase { … }
A component will be annotated with the @ListenerFor
annotation to indicate that it is subscribing to a particular set of events. Therefore, we will have two renderers that act as listeners for particular events and that implement the ComponentSystemEventListener
interface (a detailed description of this interface is available at http://blogs.sun.com/rlubke/entry/jsf_2_0_new_feature1, but as a quick description, system events are new in JSF 2.0, and there are system events that are global and others that are related to a component. They are created at various moments of application or request lifetime).
Let's see what this looks like:
@ListenerFor(systemEventClass=AfterAddToParentEvent.class, sourceClass=UIOutput.class) public class MyRenderer extends Renderer implements ComponentSystemEventListener { … public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { UIComponent component = event.getComponent(); FacesContext context = FacesContext.getCurrentInstance(); String target = (String)component.getAttributes().get("target"); if (target != null) { context.getViewRoot().addComponentResource(context, component, target); } } … }
Once you have annotated a class as a managed bean, it can be referred to as a bean with #{beanName.foo}
, where beanName
is class name (except packages) with the first letter changed to lower case, and "foo" is either an exact method name or a shortcut for a getter and setter method.
Regarding managed beans scopes we have:
@RequestScope
: (this is the default scope of a managed bean). This puts the bean in request scope. It makes a new instance for every HTTP request. Commonly, the bean is instantiated twice, once when form is displayed and once when it is submitted.
@SessionScope
: This puts a Serializable
bean in session scope. When the same user with the same cookie returns then the same bean instance is used (for this, the session timeout should not be expired).
@ApplicationScoped
: This puts the bean in application scope. All users will have access to this bean, therefore the bean either should have no state or you must manually and carefully synchronize access to it.
@ViewScoped
: This puts the bean in view scoped. The same bean instance is used as long as the same user is on same page (for example, with Ajax).
@CustomScope
: This puts the bean in custom scope. The bean is stored in the Map
, and the developer can control its lifecycle.
@NoneScope
: The bean is not put in a scope. Commonly these beans are referenced by other beans that are in scopes.
Once a component is created, it will be added as a child to another component. Before returning from the add()
method, the component will be checked for @ResourceDependency
annotations (both versions). When the @ResourceDependency
is found a new UIOutput
component instance is created. The ResourceHandler
is queried for an appropriate Renderer
based on the content type of the resource. In our case this is text/css
, therefore the style sheet renderer will be used as the Renderer
for this UIOutput
component.
The values of the name, library
(optional), and target
(optional) attributes from the annotation are stored in the component's attribute map. UIViewRoot.addComponentResource()
is called passing in the UIOutput
and the value of the target
attribute from the annotation (if exists).
Now when we render the view, for the head renderer we encode each of the resources that have been targeted for the head, like so:
… UIViewRoot viewRoot = context.getViewRoot(); for (UIComponent ui_comp:viewRoot.getComponentResources(context, "head")) {ui_comp.encodeAll(context);} …
When the Renderer
for this component is obtained it is queried for @ListenerFor
annotations. For each annotation, the Renderer
will be added as a component listener for the corresponding event. Going further, when the component is added in the tree, the event is invoked and the processEvent
method will be called for adding the component as a resource to the VewRoot
with the corresponding target.