{tip:title=Relu}Relecture RB faite le 16/02/2011{tip}
Ce chapitre n'a pas la prétention d'être une formation à _Spring_. Pour plus d'informations, vous pouvez vous reporter à la documentation de [springsource|http://www.springsource.org/] ( [HTML|http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/html] / [PDF|http://static.springsource.org/spring/docs/3.1.0.M1/spring-framework-reference/pdf/spring-framework-reference.pdf] ). Ne sont abordés ici que quelques détails permettant de mieux comprendre certains éléments des fichiers de configuration de _esup-commons_.
{note}{_}Esup-commons V2_ utilise Spring 3{note}
Tout au long de ce chapitre nous allons nous appuyer sur un exemple (configuration du gestionnaire d'exceptions) :
\\
{code}
<bean id="exceptionServiceFactory"
class="org.esupportail.commons.services.exceptionHandling.CachingEmailExceptionServiceFactoryImpl"
parent="abstractApplicationAwareBean">
<property name="smtpService" ref="smtpService" />
<property name="recipientEmail" value="${exceptionHandling.email}" />
<property name="exceptionViews">
<map>
<entry key="java.lang.Exception" value="go_exception" />
</map>
</property>
<property name="logLevel" value="${exceptionHandling.logLevel}" />
<property name="cacheManager" ref="cacheManager" />
<property name="cacheName" value="" />
</bean>
{code}
----
h4. Sommaire :
{toc:maxLevel=3}
----
h2. Les fichiers de configuration
_Spring_ permet de créer des objets (appelés alors _beans_) en les déclarant dans un fichier de configuration _XML_.
Le fichier de configuration principal (*properties/applicationContext.xml*) est déclaré dans le \*web.xml\* sous la forme d'un paramètre de l'application :
{code}
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:/properties/applicationContext.xml</param-value>
</context-param>
{code}
Dans _esup-commons_ ce fichier de configuration principal contient seulement des inclusions de fichiers de configuration spécialisés par domaine, comme par exemple:
{code}
<import resource="exceptionHandling/exceptionHandling.xml" />
{code}
Il est possible, suivant les besoins de votre application, de supprimer ou d'ajouter des fichiers de configuration spécialisés.
h2. L'injection de données
Une des caractéristiques de base de _Spring_ est de permettre l'injection de données.
{note}Cette fonctionnalité est aussi possible avec _JSF_, qui est également utilisé dans _esup-commons_, mais _JSF_ est moins puissant que _Spring_ dans ce domaine. _esup-commons_ n'utilise donc pas l'injection de données de _JSF_.
{note}
L'injection de données permet de renseigner des attributs d'un _bean_ via un fichier de configuration. Le _bean_ doit disposer d'un _setter_ pour l'accès à ces attributs.
Voyons quelques exemples...
h3. Injection d'une chaîne de caractères
{code}
<property name="recipientEmail" value="webmaster@domain.edu"/>
{code}
Dans ce cas, la méthode *setRecipentEmail()* sera appelée avec pour paramètre, la valeur *webmaster@domain.edu*.
h3. Injection d'un autre bean
{code}
<property name="authenticationService" ref="authenticationService"/>
{code}
On voit ici un autre aspect important de _Spring_ qui est l'utilisation quasi systématique des interfaces. La classe *CachingEmailExceptionServiceFactoryImpl* (qui correspond au _bean_ *exceptionServiceFactory* et contient la définition de cette propriété *authenticationService*) a un attribut *authenticationService* de type *AuthenticationService*. *AuthenticationService* est une interface. Le _bean_ *authenticationService* doit donc être d'une classe qui implémente cette interface. Ceci permet d'avoir plusieurs implémentations possibles pour cette interface et de choisir, simplement en modifiant un fichier de configuration, laquelle on utilise. Cette approche est particulièrement intéressante : elle permet, par exemple, de très facilement tester une couche de l'application en branchant des implémentations de tests des autres couches avec lesquelles le _bean_ doit interagir.
h3. Injection d'une map
{code}
<property name="exceptionViews">
<map>
<entry key="java.lang.Exception" value="go_exception" />
</map>
</property>
{code}
h3. Injection d'une liste
{code}
<property name="servers">
<list>
<ref bean="smtpServer1" />
<ref bean="smtpServer2" />
</list>
</property>
{code}
h2. Utilisation de paramètres
Afin de centraliser la configuration, une bonne pratique consiste à utiliser un fichier de configuration regroupant les paramètres de l'application. Ceci évite notamment de devoir modifier n fichiers différents.
Exemple d'utilisation :
{code}
<property name="recipientEmail" value="${exceptionHandling.email}" />
{code}
Ici la propriété _recipientEmail_ contiendra la valeur contenue dans le paramètre _exceptionHandling.email_.
Le paramètre _exceptionHandling.email_ est défini dans le fichier *default.properties* et peut être surchargé dans le fichier *config.properties* :
{code}
exceptionHandling.email=bugs@domain.edu
{code}
{note}
Ce mécanisme est rendu possible par la définition d'un bean utilisant la classe _PropertyPlaceholderConfigurer_ de Spring dans le fichier *properties/applicationContext.xml*
{code}
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/properties/defaults.properties</value>
<value>classpath:/properties/config.properties</value>
<value>file:${application.config.location}</value>
</list>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
{code}
Ici on définit les propriétés dans le fichier *defaults.properties*.Elles sont éventuellement écrasées par celles définies dans *config.properties*.
Ici on utilise aussi la possibilité de surcharger ces valeurs par une référence à un fichier qui sera précisé par une option au lancement de la JVM (Ex : *\-Dapplication.config.location=/tmp/foo.properties*). Afin que le propertyConfigurer ne lève pas une exception au cas où cette dernière possibilité n'est pas utilisée on positionne une des ces propriétés (*ignoreResourceNotFound*) pour qu'il ignore les éventuelles ressources absentes.
{note}
h2. L'héritage de configuration
_Spring_ n'oblige pas à saisir, dans toutes les définitions de _beans_, les mêmes propriétés. Pour cela, il est possible d'utiliser le mot-clé *parent*.
Le « parent » a un attribut *abstract="true"* car il ne doit pas être créé en mémoire par _Spring_. Cette notation permet de se rapprocher de l'héritage _Java_ qui est beaucoup utilisé dans _esup-commons_.
Exemple d'un _bean_ « parent » ayant aussi lui-même un « parent » :
{code}
<bean
id="abstractApplicationAwareBean"
parent="abstractI18nAwareBean"
abstract="true">
<property name="applicationService" ref="applicationService" />
</bean>
{code}
{note}L'attribut *scope* (voir ci-après) n'est pas héritable, il est donc inutile de le préciser pour un _bean_ abstrait.
{note}
h2. Vérification des _beans_
Les _beans_ manipulés par _Spring_ n'ont pas, par défaut, de dépendances particulières avec _Spring_.
Il est, par contre, possible de sciemment introduire une dépendance avec _Spring_ pour obtenir des services supplémentaires.
Faire en sorte que votre _bean_ implémente l'interface *InitializingBean* de _Spring_ en fait partie. Cette interface vous oblige à implémenter une méthode *afterPropertiesSet* qui sera appelée par _Spring_ juste après l'initialisation du _bean_. Cette méthode vous permet de vérifier que toutes les propriétés sont bien initialisées. Si ce n'est pas le cas, vous pouvez, par exemple, lever une exception ou affecter une valeur par défaut.
On trouvera par exemple :
{code}
public void afterPropertiesSet() {
super.afterPropertiesSet();
if (!StringUtils.hasText(exceptionView)) {
exceptionView = DEFAULT\__SERVLET_\_EXCEPTION_VIEW;
logger.info(getClass() + ": no exceptionView set, using default \[" + exceptionView + "\]");
}
}
{code}
h2. Portée des beans
_Spring_ offre une notion de portée (*scope*).
Par défaut, un _bean_ est de portée *singleton*. _Spring_ crée une seule instance de ce _bean_ pour toute la durée d'exécution de l'application.
Il existe aussi des portées *session* et *request* qui, respectivement, permettent d'avoir une instance du _bean_ par session utilisateur (au sens d'une application web) ou par requête (au sens HTTP). Cette notion est particulièrement intéressante pour les contrôleurs web d'une application. Ces derniers sont en général des _beans_ de portée *session*, comme par exemple :
{code}
<bean id="administratorsController"
class="[...].formation.web.controllers.AdministratorsController"
parent="abstractContextAwareController"
scope="session" />
{code}
Usuellement, un _bean_ de _scope_ *request* peut faire référence, via ses propriétés, à un _bean_ de _scope_ *session* ou *singleton*. De même, un _bean_ de _scope_ *session* peut faire référence à un _bean_ de _scope_ *singleton*. Une bonne architecture nous amène d'ailleurs à utiliser dans ce sens l'injection de beans. Par défaut, la réciproque provoque une exception ... mais il est cependant possible de réaliser cette réciproque par le biais de l'_aop_, et cela très simplement.
Concrètement si nous voulons ici injecter le _bean_ *administratorsController* qui est de scope session dans un bean de scope singleton, on utilisera la balise _aop:scoped-proxy_ comme ceci dans la déclaration du _bean_ *administratorsController* :
\\
{code}
<bean id="administratorsController"
class="[...].formation.web.controllers.AdministratorsController"
parent="abstractContextAwareController"
scope="session">
<aop:scoped-proxy/>
</bean>
{code}
Pour ce faire, on aura pris soin de déclarer comme il se doit l'espace de noms aop, avec dans la balise racine du fichier de configuration de _beans Spring_ ceci :
{code}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd" >
...
</beans>
{code}
{HTMLcomment}
h2. Récupération des _beans_
Il est possible de récupérer un _bean_ à partir de son nom à partir du code JAVA. La classe *BeanUtils* fournie par _esup-commons_ V1 n'est plus utilisée.
On utilise un mécanisme interne à Spring basé sur l'implémentation de *org.springframework.context.ApplicationContextAware*
_esup-commons_ V2 fournit une telle implémentation qu'il suffit de déclarer dans un fichier de configuration Spring (ex : *properties/applicationContext.xml*) :
{code}
<bean id="app_context" class="org.esupportail.commons.context.ApplicationContextHolder"/>
{code}
Depuis le code java, on utilisera :
{code}
ApplicationContext springContext = ApplicationContextHolder.getContext();
DomainService domainService = (DomainService) springContext.getBean("domainService");
{code}
{warning}* L'utilisation de cette méthode devrait rester exceptionnelle (mode _batch_ par exemple)
{warning}{HTMLcomment} |