| Balise Wiki | ||
|---|---|---|
{tip:title=Relu}Relecture RB faite le | ||
| Astuce | ||
| ||
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_. |
...
| Remarque |
|---|
{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) |
...
| Bloc de code |
|---|
: \\ {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> |
Sommaire :
| Sommaire | ||
|---|---|---|
|
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.
...
{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>
|
Utilisation de paramètres
...
{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
| |
| Remarque | |
|---|---|
Ce mécanisme est rendu possible par la définition {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 Bloc de code | * {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> </list> </property> </bean> {code} {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 |
...
| Bloc de code |
|---|
un « parent » :
{code}
<bean
id="abstractApplicationAwareBean"
parent="abstractI18nAwareBean"
abstract="true">
<property name="applicationService" ref="applicationService" />
</bean>
|
| Remarque |
{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. |
Vérification des beans
...
{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 + "\]");
}
}
|
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 :
| Bloc de code |
|---|
{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 |
...
| Bloc de code |
|---|
* : \\ {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: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"> |
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.
...
{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} |