Introduzione
Nel post Utilizzo di CDI in JSF 2.0 abbiamo visto come utilizzare la Dependency Injection, resa disponibile dal framework J2EE nella specifica JSR-299, ed implementata in JBoss Weld. In particolare abbiamo visto come integrarla con JSF 2.0. In questo articolo seguiremo un percorso analogo per integrare l’IoC Container fornito dalla componente core del framework Spring, descritto nel post Primi Passi con Spring (core).
Una caratteristica fondamentale di Spring è quella di essere un framework che non forza il programmatore all’utilizzo di una particolare architettura o tecnologia o metodologia di sviluppo. Questo è particolarmente evidente nell’ambito delle applicazioni web in cui Spring fornisce un suo framework di riferimento (Spring MVC) ma, nonostante ciò, continua a supportare l’integrazione con altri popolari framework per il web, ed in particolare con JSF.
Come progetto di esempio utilizzeremo lo stesso progetto employees già descritto in diversi post, in particolare in Create Web App con Bootstrap e Utilizzo di CDI in JSF 2.0. Si tratta di un progetto web creato con Maven e configurato per essere eseguito sull’application server WildFly di JBoss.
Configurazione del POM
Per utilizzare Spring con JSF configuriamo innanzitutto le dipendenze nel file pom.xml
del nostro progetto employees di esempio.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<!-- Option 1: Use if deploying to a Java EE application server (GlassFish, JBoss AS, etc) --> <dependency> <groupId>javax.faces</groupId> <artifactId>jsf-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <!-- Spring framework --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.3.2.RELEASE</version> </dependency> |
Spring Container
Tipicamente in una architettura web esiste uno strato di presentazione, composto dalle interfacce utente e dai bean di interazione con esse, ed uno strato di back-end, che fornisce servizi di business specifici e l’accesso ai dati. In Spring questo layer esiste all’interno di un contesto di business specifico che non contiene alcun elemento di presentazione indicato col nome di business context. Anche in Spring MVC, ad esempio, esiste un contesto distinto che è specifico del layer di presentazione denominato presentation context.
Vediamo ora come configurare il container di Spring per il contesto di business. Si noti che questo passo di configurazione non è specifico dell’integrazione con JSF, infatti si applica a tutti i framework web, incluso Spring MVC. A tale scopo tutto ciò che dobbiamo fare è quello di dichiarare il listener ContextLoaderListener
nel file web.xml
della propria applicazione web, e aggiungere una sezione <context-param>
(nello stesso file) che definisce quale set dei file XML devono essere utilizzati per la configurazione di Spring.
1 2 3 4 5 6 7 8 |
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/applicationContext*.xml</param-value> </context-param> |
Se non si specifica il parametro contextConfigLocation
, il ContextLoaderListener
cercherà un file chiamato /WEB-INF/applicationContext.xml
da caricare. Una volta che i file di contesto vengono caricati, Spring crea un oggetto WebApplicationContext
in base alle definizioni dei bean e lo memorizza nella ServletContext
dell’applicazione web. L’accesso al business context avviene quindi recuperando l’ApplicationContext
creato dal ContextLoaderListener
e memorizzato nel ServletContext
:
1 |
WebApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(servletContext); |
Integrazione con JSF
La componente chiave per l’integrazione con JSF di Spring è la classe SpringBeanFacesELResolver
. Si tratta di un JSF compliant ELResolver che delega la risoluzione delle espressioni EL al business context di Spring (WebApplicationContext
) e solo successivamente all’implementazione JSF. Per farlo utilizziamo il file faces-config.xml
:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1"> <application> <el-resolver> org.springframework.web.jsf.el.SpringBeanFacesELResolver </el-resolver> </application> </faces-config> |
Per proseguire con la configurazione del nostro progetto possiamo seguire due strade: utilizzare i file XML o le annotazioni. Vediamo nel dettaglio entrambe i casi.
Utilizzo degli XML
In questo caso utilizziamo i file faces-config.xml
ed applicationContext.xml
per definire rispettivamente i managed bean gestiti dall’IoC context di JSF ed i bean gestiti da Spring. Iniziamo quindi con Spring e definiamo il servizio EmployeeService
:
1 2 3 4 5 6 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="employeeService" class="it.inspiredsoft.service.EmployeeService"/> </beans> |
Quindi utilizziamo il bean così definito per iniettarlo nel managed bean employeeManagedBean
che andremo a configurare in faces-config.xml
nel modo seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_1.xsd" version="2.1"> <application> <el-resolver> org.springframework.web.jsf.el.SpringBeanFacesELResolver </el-resolver> </application> <managed-bean> <managed-bean-name>employeeManagedBean</managed-bean-name> <managed-bean-class>it.inspiredsoft.bean.EmployeeManagedBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> <managed-property> <property-name>employeeService</property-name> <value>#{employeeService}</value> </managed-property> </managed-bean> </faces-config> |
Diversamente dal caso di integrazione con CDI, nella classe EmployeeManagedBean
dovrà essere presente un metodo setter utilizzato dal resolver di Spring per iniettare il bean employeeService
.
1 2 3 4 5 6 7 8 9 |
public class EmployeeManagedBean implements Serializable { // Servizio iniettato da Spring EmployeeService employeeService; public void setEmployeeService(EmployeeService employeeService) { this.employeeService = employeeService; } .... } |
Il codice sorgente dell’applicazione è disponibile in sping employees. Una volta deployata su WildFly aprite il browser all’indirizzo http://localhost:8080/sping-employees/.
Utilizzo delle Annotazioni
Se preferiamo utilizzare le annotazioni al posto dell’xml per la configurazione del nostro progetto, modifichiamo innanzitutto il file applicationContext.xml
di Spring per configurare l’autoscan:
1 2 3 4 5 6 7 8 9 10 11 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <context:component-scan base-package="it.inspiredsoft" /> </beans> |
Quindi annotiamo la classe EmployeeService
con @Service
per farla diventare un bean gestito dal contesto IoC di Spring.
Ed infine annotiamo EmployeeManagedBean
con le annotazioni @ManagedBean
e @SessionScoped
(avendo cura di rimuovere la definizione del bean nel file faces-config.xml
) per definirle come componente managed di JSF. A queste annotazioni va aggiunta @Component
, che rende il bean “visibile” anche a Spring e l’annotazione @Autowired
per indicare a Spring il punto di iniezione del bean employeeService
. La classe si presenta quindi così:
1 2 3 4 5 6 7 8 9 10 11 |
@Component @ManagedBean @SessionScoped public class EmployeeManagedBean implements Serializable { // Servizio iniettato da Spring @Autowired EmployeeService employeeService; .... } |
@Autowired
non vi è necessità del metodo setter.
Il codice sorgente è disponibile in sping employees. Una volta deployata su WildFly aprite il browser all’indirizzo http://localhost:8080/sping-employees/.
In realtà l’utilizzo misto delle annotazioni @ManagedBean
e @Component
non è necessario. In particolare l’annotazione JSF può essere rimossa e l’esempio funziona ugualmente. Ovviamente questo rende il bean non più gestito dal’IoC di JSF che quindi non potrà essere iniettato in un altro managed bean se non da Spring. Le interfacce continuano a funzionare perché il resolver che abbiamo definito nel file faces-config.xml
accede innanzitutto al contesto di Spring per la risoluzione delle espressioni.
JSR-330
Dalla versione 3.0, Spring offre il supporto allo standard JSR-330. Questo implica che è possibile utilizzare l’annotazione @Inject
al posto di @Autowired
e @Named
al posto di @Component
. Ciò rende l’applicazione più portabile verso altri ambienti.