JSF injection with managed property, good pattern?

Qualaelay picture Qualaelay · Sep 7, 2012 · Viewed 14.1k times · Source

I'm quite new to JSF and not really "used" to the different thinking so I'm struggling on what (I assume) is basic.

Lets say I have a class User, which is a session bean.

Lets say I have a controller of 10000 objects, say Factory, that needs to be able to set some of them as "locked", in our case it means that the "locked" field does not become null anymore but reference a "LockedItem" object.

This is where I can't get things working : LockedItem, when you instanciate it, is supposed to reference the user currently logged in. How am I supposed to do that ?

I tried injection with @managedproperty, but it is null in LockedItem.constructor (which is normal I assume) then I tried in a @PostConstruct method, but that method is never called (why ? Even if I make it a managedbean... are the postconstruct methods only called when the object is created by the ".xhtml" ?) Or should I use a "java se" trick, like making the User static ?


Code to clarify why is a @PostConstruct not called (the one of "Seat") :

.xhtml

<h:outputLabel id="user" value="Hello #{user.name}" />
<h:outputLabel id="car" value="you have #{car.brand}" />

User

package test;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
public class User implements Serializable {
    private String name ;

    public User()
    {
        name = "toto"; 
        System.out.println("User constructor");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


}

Car

package test;

import java.io.Serializable;

import javax.faces.bean.ManagedBean;

@ManagedBean
public class Car implements Serializable {
    private String brand ;
    private Seat seat ;

    public Car()
    {
        brand = "audi" ;
        seat = new Seat();
        System.out.println("Car constructor") ;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }


}

Seat

package test;

import java.io.Serializable;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.ManagedProperty;

@ManagedBean
public class Seat implements Serializable {
    private int nb ;
    private String userName ;

    @ManagedProperty("#{user}")
    private User user ;

    public Seat()
    {
        nb = 4 ;
        userName="na";
        System.out.println("! Seat constructor ") ;
    }

    @PostConstruct
    public void init()
    {
        System.out.println("!! Seat postconstruct : "+user.getName());
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public int getNb() {
        return nb;
    }

    public void setNb(int nb) {
        this.nb = nb;
    }
}

Thanks !

Answer

BalusC picture BalusC · Sep 7, 2012

The @PostConstruct is the right way.

It's not called if you instantiate the bean yourself using new operator (obviously). It's only called if JSF instantiates and manages the bean itself whenever it's referenced for the first time in EL context like so #{bean}. This indeed usually happens in the view side, but this can also happen in model/controller side by @ManagedProperty("#{bean}") or Application#evaluateExpressionGet().

You should absolutely not make the User static. It would be shared applicationwide, not sessionwide.

An alternative is to just pass the current User as constructor argument of LockedItem, or to invoke the initialization method yourself, for sure if that class does not represent a legit JSF backing bean at all.