Spring’iser complètement une application JSF

Spring peut facilement être intégré à une application JSF (voir ce billet).
Ainsi, Les managed-beans de JSF pourront disposer des fonctionnalités offertes par Spring et spécialement la DI (Dependancy Injection) via l’utilisation des EL dans faces-config.xml.
Avec la version 2.5, Spring permet d’aller encore plus loin en prenant en charge la gestion complète des managed beans de JSF.

Pour ce faire, il
faut comme d’habitude déclarer le chargeur de contexte de Spring dans le web.xml, comme ceci:

<listener>	
		<listener-class>	
				org.springframework.web.context.ContextLoaderListener	
		</listener-class>	
</listener>

Ensuite, on applique au contrôleur une autre annotation comme-ci:

@Controller	
@Scope("request")	
public class TestCtrl	
:				
:	

Il faut aussi créer l’habituel applicationContext.xml dans WEB-INF:

<?xml version="1.0" encoding="UTF-8"?>	
<beans xmlns="http://www.springframework.org/schema/beans"	
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"	
		xmlns:p="http://www.springframework.org/schema/p"	
		xmlns:context="http://www.springframework.org/schema/context"	
		xmlns:tx="http://www.springframework.org/schema/tx"	
		xsi:schemaLocation="http://www.springframework.org/schema/beans	
				http://www.springframework.org/schema/beans/spring-beans-2.5.xsd	
				http://www.springframework.org/schema/context
				http://www.springframework.org/schema/context/spring-context-2.5.xsd">
	
		<!--	
		  Active la prise en charge par Spring de diverses annotations.	
		-->	
		<context:annotation-config />	
	
		<!--	
		  Active le scan par Spring des beans pour diverses annotations.	
		  On spécifie le package de base où effectuer le scan.	
		-->	
		<context:component-scan base-package="org.djo.control" />	
</beans>

On passe à l’étape suivante (montré dans un précédent billet): L’intégration de Spring dans faces-config.xml:

<?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_1_2.xsd"
	   version="1.2">	
		<application>	
				<el-resolver>	
				org.springframework.web.jsf.el.SpringBeanFacesELResolver	
				</el-resolver>	
		</application>	
</faces-config>

On crée ensuite un contrôleur (le feu managed-bean):

package org.djo.control;	

import org.springframework.stereotype.Controller;

@Controller("test")	
public class TestCtrl {	
		private String msg = "Hello ";	
	
		//getter et setter de msg	
}

Remarquez l’annotation @Controller (stéréotype dans la terminologie Spring) qu’on a appliqué à cette classe.

Cette annotation sera interceptée par Spring (grâce à l’élément component-scan décrit plus haut et le cycle de vie de ce bean sera alors géré par Spring et non plus par l’implémentation JSF.
Ce bean sera alors exposé aux pages JSF (et partout ailleurs, comme tout autre bean managé par Spring) mais disposera en plus de tous ce que Spring offre comme la DI et la gestion des transactions, des annotations JSR 250 (gestion du lifecycle), etc.

On peut faire le test suivant dans une page JSF:

<h:outputText value="#{test.msg}" />	

On aura “Hello” comme résultat si tout va bien.

Approfondissons encore la chose !
La première question qui se pose est comment contrôler le scope du contrôleur ?
Par défaut, il s’agit du scope Singleton (equivalent à ApplicationScope).
Pour pouvoir utiliser les scopes ordinaires du web (request et session), il faut déclarer un autre listener dans le web.xml:

<listener>	
		<listener-class>	
				org.springframework.web.context.request.RequestContextListener	
		</listener-class>	
</listener>

Il suffit ensuite d’ajouter l’annotation @Scope pour contrôler finement le scope d’un contrôleur:

@Controller	
@Scope("request")	
public class TestCtrl 	
:	
:

Vous pouvez utiliser les scopes suivants:

  • singleton
  • prototype
  • session
  • request

Remarquez que de cette façon, il devient complètement inutile de déclarer les contrôleurs dans faces-config.xml.

Etant managé par Spring, il devient possible d’utiliser les annotations de la JSR 250 comme @PostConstruct et @PreDestroy.

Une méthode annotée avec @PostConstruct est appelée automatiquement par Spring suite à l’instantiation du bean et la satisfaction de toutes ses dépendances et une méthode annotée avec @PreDestroy est appelée automatiquement par Spring avant la destruction du bean.

La première annotation est utile par exemple dans le scénarion suivant:

Le contrôleur déclare un champ EntityDao (un DAO) comme dépendance et possède une liste List qu’il expose aux pages JSF.
Le contrôleur ne peut pas utiliser le DAO pour initialiser la liste dans le constructeur vu que le DAO n’est pas encore injecté.

La solution est alors de le faire dans une méthode annotée avec @PostConstruct.

Advertisements

12 Responses to Spring’iser complètement une application JSF

  1. rudy says:

    Bonjour,

    Il manque un guillement dans ton applicationContext.xml après le :
    http://www.springframework.org/schema/context/spring-context-2.5.xsd

    Rudy.

  2. rudy says:

    Il y aussi une erreur dans ta classe TestCtrl qui devrait s’écrire de la manière suivante :

    package org.djo.control;
    import org.springframework.stereotype.Controller;

    @Controller(“test”)
    public class TestCtrl
    {
    private String msg = “Hello “;

    public String getMsg() {
    return msg;
    }

    public void setMsg(String msg) {
    this.msg = msg;
    }

    //getter et setter de msg
    }

  3. jawher says:

    Bonjour Rudy,

    Merci pour les correctifs, ils sont intégrés.
    J’en ai aussi profité pour ajouter la coloriation syntaxique.

  4. rudy says:

    Bonjour,

    J’ai essayé, ça ne fonctionne pas pour moi. Et pourtant je n’ai aucun message d’erreur à la compilation.

    Je n’ai pas déclaré le controller TestCtrl dans aucun fichier XML. Tu penses que c’est ça le problème ? Je pense à bean.xml par exemple.

    Merci.

    rudy.

    • jawher says:

      Dans appplicationContext.xml, tu as bien modifié la valeur de base-package pour l’adapter à ton cas ?

      Sinon, Assures toi qui tu as configuré log4j pour que les erreurs soient affichées dans la console lors du démarrage du serveur.

  5. rudy says:

    Bingo : bien vu…

    C’était bien ça.

    C’est compliqué d’installer log4j ? Parce qu’autant je trouve le java EE et l’open source de manière générale très pratique, évolué et agréable (car gratuit ;)), par contre les StackTrace sont plutôt compliqués à déchiffrer. En tout cas pour les gens qui ont un an de java EE comme moi.

    Thanks a lot.

    Rudy.

  6. rudy says:

    Par contre j’ai un autre problème. Pour travailler entièrement avec Spring, springiser comme tu dis, j’ai passé tous mes @ManagedBean à des @Controller.

    Et lors de l’appel d’un controller dans mon premier formulaire, j’ai le message d’erreur suivant :

    “Error creating bean with name ‘utilisateurController’: Scope ‘session’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request? If you are actually operating within a web request and still receive this message,your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.”

  7. rudy says:

    J’ai rajouté :

    org.springframework.web.context.request.RequestContextListener

    dans le web.xml et maintenant j’ai l’erreur suivante :

    Error creating bean with name ‘utilisateurController’: Injection of resource fields failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [jsf.facade.UtilisateurFacade] is defined: Unsatisfied dependency of type [class jsf.facade.UtilisateurFacade]: expected at least 1 matching bean

  8. rudy says:

    Dans appplicationContext.xml, j’ai bien modifié la valeur de base-package pour l’adapter à mon cas, c’est à dire au répertoire jsf.controller.

    Mais dans mes controller il y a des EJB façades qui se trouvent dans le répertoire jsf.facade.

    Dois-je rajouter tous mes ressource packages dans l’appplicationContext.xml et comment ?

    Merci.

    Rudy.

  9. rudy says:

    Merci pour le lien, ça a l’air pas mal.

    Pour tes applications, tu gères la persistance comment alors, sans EJB ?

    • jawher says:

      Ca dépend du projet, mais généralement Spring JDBC, iBatis, JPA/Hibernate.

      Pour revenir sur JPA, ce dernier ne fait plus partie des EJBs et est maintenant uen solution de persistance autonome utilisable même dans une application Java SE.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: