L’alternativa a Java EE: come implementare lo stack JSF+Spring+Hibernate con Java SE e Tomcat – Parte II

Nella prima parte 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 KeepAlive di RichFaces.

  • @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 parte di configurazione del dominio.

Dalla logica di business al database

Abbiamo già visto come configurare Hibernate e Spring in modo che collaborino, sia tramite wrapper proprietari (ovvero HibernateTemplate) o tramite il supporto al JPA (implementando EntityManager).

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 con DataAccessException.

Oltre che implementare l’interfaccia UserDao, questa classe estende la classe generica GenericDao<T>: 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’attributo JdbcTemplate che sul metodo setUpHibernateTemplate. In entrambe i casi vengono iniettate le istanze configurate nel file XML.

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 unitName è definito nel persistence.xml.

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).

Andrea Como

Sono un software engineer focalizzato nella progettazione e sviluppo di applicazioni web in Java. Presso OmniaGroup ricopro il ruolo di Tech Leader sulle tecnologie legate alla piattaforma Java EE 5 (come WebSphere 7.0, EJB3, JPA 1 (EclipseLink), JSF 1.2 (Mojarra) e RichFaces 3) e Java EE 6 con JBoss AS 7, in particolare di CDI, JAX-RS, nonché di EJB 3.1, JPA2, JSF2 e RichFaces 4. Al momento mi occupo di ECM, in particolar modo sulla customizzazione di Alfresco 4 e sulla sua installazione con tecnologie da devops come Vagrant e Chef. In passato ho lavorato con la piattaforma alternativa alla enterprise per lo sviluppo web: Java SE 6, Tomcat 6, Hibernate 3 e Spring 2.5. Nei ritagli di tempo sviluppo siti web in PHP e ASP. Per maggiori informazioni consulta il mio curriculum pubblico. Follow me on Twitter - LinkedIn profile - Google+