Skip to main content

JSF 1.2 RI - Bean Instantiation and Annotations

Posted by jhook on May 1, 2007 at 3:38 PM PDT

JavaServer Faces 1.2 includes support for Resource Injection and annotations such as @PostConstruct and @PreDestroy. Coming from action frameworks, these 'implicit' hooks make composite presentations a bit easier to pull together, even in the stateless HTTP GET case.

JavaServer Faces currently has a couple great frameworks that extend application coordination and ease development. Since not everyone uses these frameworks, I wanted to write a short blog on what you can do out of the box with JavaServer Faces 1.2 and the reference implementation.

Push vs. Pull in Views

Since JSF's inception, you've been able to declare any number of java objects within multiple faces-config.xml files as termed 'managed beans'. Traditionally with action frameworks, each request is primarily sourced from a single action instance-- anything it can create is pushed into existence for the view to use. With JSF's managed beans, you can compose a series of stand-alone objects which can be pulled into the view in any quantity.

Sometimes it's difficult with action frameworks to be able to setup (push) everything the view may or may not need at the time of evaluation. When you have something like managed beans and EL within JSF, nothing needs to be pre-executed as your view can just pull in the managed beans it needs.

A simple use case is displaying data conditionally in the view-- happens all the time. Since you can pull only what you need into your view, there's less concern of pushing unnecessary database state into a view which may not actually need it. A second case is reuse. You may need to generate a list of current orders in multiple places. With JSF, the practice is to just create a 'CurrentOrdersBackingBean' which can be pulled into any view. Often many from the Struts world would've populated that list within any possible action that results in a view which may display the list of orders.

Basically, the development mentality with a pull MVC is to create fine-grained beans with a single purpose (SRP). JSF's managed bean declarations will allow you to wire in other state from session beans or request parameters. In the case of displaying a list of orders, you may end up with a config like so:

<managed-bean>
  <managed-bean-name>ListOrdersBean</managed-bean-name>
  <managed-bean-class>ex.view.ListOrdersBean</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>credentials</property-name>
    <value>#{AuthManager}</value>
  </managed-property>
</managed-bean>
<managed-bean>
  <managed-bean-name>AuthManager</managed-bean-name>
  <managed-bean-class>ex.view.AuthManager</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The above configuration will allow views to use/declare ListOrdersBean and when JSF instantiates it, the AuthManager will be assigned, allowing the ListOrdersBean to pull the correct set of orders for the current set of credentials in the session.

@PostConstruct and @PreDestroy

There's a problem from a programming standpoint with the above example. Even if JSF assigns the credentials to your ListOrdersBean, when does your bean know to actually fetch the list of orders? When 'getOrders()' is called by the view? JSF has five phases to it's lifecycle-- 'getOrders()' can be called with each phase and you don't want to duplicate lazy instantiation with each call. What do you do?

Aside from hacking the if (this.orders != null) ... within the getter, JavaServer Faces 1.2 has the @PostConstruct which gets called before returning the bean for use.

public class ListOrdersBean {
    private List orders;
    private Credentials credentials;

    @PostConstruct
    public void init() {
       if (this.credentials != null) {
         this.orders = ....;
       } else {
         this.orders = Collections.EMPTY_LIST;
       }
    }

    public List getOrders() {
       return this.orders;
    }
   
    public void setCredentials(Credentials cred) {
       this.credentials = cred;
    }
}

The init() method will be called once for instantiating the request-scoped bean and succeeding calls to getOrders() will not need to have a bunch of lazy initialization code.

You can take this one step further with List/Detail views and
stateless HTTP GET requests (/employee.jsf?id=456).

<managed-bean>
  <managed-bean-name>EmployeeFinder</managed-bean-name>
  <managed-bean-class>ex.view.EmployeeFinder</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <managed-property>
    <property-name>id</property-name>
    <value>#{param.id}</value>
  </managed-property>
</managed-bean>

Notice that we can instantiate this backing bean for the request and have JSF automatically assign a request parameter (id) to the backing bean.

public class EmployeeFinder {
    private long id;
    private Employee result;
   
    @PostConstruct
    public void findEmployee() {
        if (this.id > 0) {
           this.result = ...;
        }
    }

    public void setId(long id) {
        this.id = id;
    }

    public Employee getResult() {
        return this.result;
    }
}

Your employee.html page can then simply just use #{EmployeeFinder.result} to display the employee that matches the id parameter passed. If none was found, you can just display an error message without producing a second page (as is a traditional practice with action frameworks).

Who says every JSF request has to be a POST?

Another good point is that this granularity of backing beans without Servlet API dependencies makes unit testing much more practical.

Resource Injection

I'm not going to discuss resource injection because there's a bunch of other great articles/blogs on the topic [1]. But the flow within the JSF 1.2 RI for instantiating managed beans is as follows:

  1. New the bean declared using the default constructor
  2. Inject any @Resource declarations
  3. Assign any managed-properties declared, cascading this process if new beans are referenced
  4. Call @PostConstruct on the bean if declared
  5. Store the bean in the correct scope and return

With the JSF 1.2 RI, you can get the pre/post events outside of a Java EE 5 container by simply having the annotations.jar in your classpath.

I hope this blog answers some questions on how to properly instantiate managed beans with JavaServer Faces 1.2, it's a big improvement over JavaServer Faces 1.1 and usable today with the RI.