Nella di questo post abbiamo presentato una configurazione alternativa e ben collaudata all’architettura Java Enterprise, senza però rinunciare ad alcune delle sue peculiarità. Una volta configurato l’ambiente come descritto nel primo post, vedremo adesso come è possibile integrare e far interagire a runtime in modo molto semplice JSF 2, Spring 3 e Hibernate 3.5.
Dalla vista alla logica di business
Una delle novità più interessanti introdotta nelle JSF 2 è il supporto alle annotazioni: addio faces-config.xml infinito e talvolta di difficile manutenzione (anche se è possibile spezzarlo in più file)! Con qualche annotazione è possibile definire una classe come backing (o managed) bean, dargli uno scope o iniettarvi l’istanza di un altro backing bean. La parola “iniettare” richiama il concetto di Dependancy Injection (DI): anche JSF, per chi non lo sapesse, gestisce un proprio Inversion of Control (IoC) container. Salta subito all’occhio un fatto: se voglio lavorare con JSF e Spring dovrò aver a che fare con 2 IoC container? Ebbene sì: uno per la gestione dei controller della vista e uno per la logica di dominio. Può sembrare ridondante, ma dal punto di vista dell’applicativo è più riusabile mantenere le due sfere separate.
Come faccio però a farle comunicare? Ovviamente Spring dà una risposta a questo quesito: basta inserire una volta per tutte nel faces-config.xml un opportuno EL resolver:
org.springframework.web.jsf.el.SpringBeanFacesELResolver
che delega a Spring tutte le espressioni EL che JSF non riesce a risolvere, tra cui gli stessi bean definiti nel container di Spring.
Vediamo un esempio pratico. Ammettiamo di avere una classe annotata come segue:
@ViewScoped
@ManagedBean(name="userBB")
public class UserBackingBean implements Serializable {
@ManagedProperty(value="#{userManager}")
private UserManager patientManager;
@PostConstruct
public void init() {
// Post construct callback
}
// Backing bean code...
public void setUserManager(UserManager userManager) {
this.userManager = userManager;
}
-
@ManagedBean(name="userBB")
: la classe viene gestita come backing bean, referenziata nelle pagine tramite il nome userBB. Se il nome non viene specificato, quello di default coincide con il nome della classe con l’iniziale minuscola. -
@ViewScoped
: oltre ai classici scope di request, session e application, JSF 2 ne dispone di nuovi. Tra questi,@ViewScoped
ci garantisce che il backing bean sia vivo tra più richieste HTTP finché non si cambia vista, proprio come fa il . -
@ManagedProperty
: JSF attiva il sistema di Dependency Injection (DI) per valorizzare la variabile. Il sistema è basato sui metodi setter dell’attributo annotato: senza di questo l’injection non funziona!! Se il l’espressione#{userManager}
non viene risolta dall’IoC di JSF, interviene quello di Spring, se abbiamo configurato l’EL Resolver. In questo esempio riusciamo ad iniettare un bean di Spring in un backing bean JSF in modo trasparente, come se si trattasse di una classe gestita da quest’ultimo container. -
@PostConstruct
: metodo di callback che viene chiamato da JSF dopo l’inizializzazione dell’oggetto e dell’injection degli attributi.
Come si può intuire dal nome, la classe UserManager
non è un backing bean, ma è definita nel container della logica di dominio (quella gestita da Spring per intendersi) come segue:
@Service(value="userManager")
public class UserManagerImpl implements UserManager {
@Autowired
private UserDao userDao;
// Class body...
}
-
@Service(value="userManager")
: la classe viene definita come un business service facade gestita dal container di Spring. Il nome è quello che viene risolto dalla EL del JSF. La classe implementa un’interfaccia (che non ha annotazioni): una delle best practices della programmazione con Spring è quella di lavorare sempre con le interfacce per disaccoppiare le implementazioni e permettere il proxying delle interfacce al framework. Con Spring infatti non avremo mai accesso diretto alle nostre implementazioni, ma queste saranno sempre mascherate da proxy e arricchite con le funzionalità che il framework offre. -
@Autowired
: la DI all’interno del container di Spring è gestita tramite questa annotazione e@Resource
, con una sostanziale differenza: la prima esegue la DI in base al tipo, mentre la seconda in base al nome del bean. L’uso congiunto di@Autowired
con@Qualifier
si comporta come@Resource
. La DI di Spring, a differenza di quella di JSF, non ha necessità dei metodi setter.
Una classe come questa può essere interessata dal transaction Manager come abbiamo già visto nell’altro post sulla .
Dalla logica di business al database
Abbiamo già visto come configurare Hibernate e Spring in modo che collaborino, sia tramite o tramite il .
Implementiamo quindi un DAO per ogni entità: anche in questo caso vale la best practice della separazione implementazione/interfaccia:
@Repository
public class UserDaoImpl extends GenericDao implements UserDao {
// CRUD methods
}
-
@Repository
: indica che la classe ha lo scopo di accedere al database. Ogni eccezione lanciata dai suoi metodi viene wrappata conDataAccessException
.
Oltre che implementare l’interfaccia UserDao
, questa classe estende la classe generica GenericDao
: se impostiamo il nostro progetto con queste gerarchie, questo sarà l’unico punto che differenzia un progetto basato su HibernateTemplate
da quello basato su EntityManager
. Vediamone le diverse implementazioni.
Generic DAO e Hibernate Template
Estendere la classe HibernateDaoSupport
è il modo classico (e forse ormai obsoleto?) di lavorare con Spring e Hibernate.
public abstract class GenericDao extends HibernateDaoSupport {
@Autowired
protected JdbcTemplate jdbcTemplate;
@Autowired
public void setUpHibernateTemplate(HibernateTemplate hibernateTemplate) {
super.setHibernateTemplate(hibernateTemplate);
}
...
}
-
L’annotazione
@Autowired
è usata in due modi: sia sull’attributoJdbcTemplate
che sul metodosetUpHibernateTemplate
. In entrambe i casi vengono iniettate le istanze .
Generic DAO e Entity Manager
Probabilmente la scelta più giusta per un progetto nel 2011, in cui si astrae dall’implementazione (Hibernate) e si “pensa” in JPA.
public abstract class GenericDao {
@PersistenceContext(unitName = "myAppPU")
protected EntityManager entityManager;
...
}
-
Come accade negli EJB 3, viene iniettata l’istanza dell’
EntityManager
di Spring tramite l’annotazione@PersistenceContext(unitName = "myAppPU")
, dove lo .
Conclusioni
Complessivamente, in due post abbiamo visto da un lato come configurare un ambiente simil-enterprise senza però il costo di risorse che esso richiede, e dall’altro l’integrazione di framework importanti come JSF, Spring e Hibernate tramite semplici annotazioni come @ManagedPropery
o @Autowired
.
Un consiglio sui nuovi progetti? Per quanto riguarda l’integrazione Spring/Hibernate mi sembra quasi scontato ormai propendere per la soluzione JPA a scapito di quella basata su hibernate template per ovvii motivi di compatibilità futura!
Per approfondire gli argomenti trattati, consiglio la lettura (o almeno la conoscenza) di questi 3 libri della serie “In Action” (purtroppo non tutti in italiano).