Spring introduit une nomination pour les objets gérés par l'injection des dépendances. Ces objets sont appelés des beans.
Qu'est-ce que c'est un bean dans Spring ?
Un bean est un objet totalement managé par inversion of control container (IoC). Ce dernier s'occupe d'initialiser et d'assembler cet objet.
Les beans sont représentés dans les méta-données de configuration qui servent ensuite à IoC dans la gestion des beans. La configuration peut être exprimée soit en fichiers XML, soit en annotations.
Composition des beans
Sur la base de notre projet de gestion de la bibliothèque, on analysera la définition d'un bean à travers un fichier XML.
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
Ce fragment de code représente la définition la plus simple du bean. On remarque la présence des 2 attributs :
- id : permet de spécifier l'identifiant du bean. Il peut exister uniquement un seul élément avec un identifiant donné.
- class : représente la classe du bean.
Les beans peuvent être appelés aussi avec l'attribut name. Cependant, une seule condition est à respecter. Un nom (bean peut en avoir plusieurs) doit être unique dans le contexte. Si un bean contient tous les attributs de nommage, l'attribut le plus important est id. Si celui-ci était absent, Spring chercherait l'attribut name. Si celui-ci n'était pas spécifié, il prendrait l'attribut class pour déterminer le nom du bean dans le contexte.
< -- 1st method -- > <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="100000"/> <property name="maxInMemorySize" value="100000"/> <property name="defaultEncoding" value="utf-8"/> </bean> < -- 2nd method -- > <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="dataSource" ref="dataSource"/> <property name="entityManagerFactory" ref="emf"/> </bean> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/library" /> <property name="user" value="root" /> <property name="password" value="root" /> </bean>
A part les deux attributs mentionnés précédemment, on retrouve une balise property. Elle représente la propriété utilisée par le bean. Chaque propriété doit posséder son propre setter dans la classe définie dans l'attribut class.
En occurrence, on présente deux méthodes de définition des propriétés :
- la première repose sur une simple précision de valeur dans l'attribut value. Par exemple, pour la propriété maxUploadSize< on retrouvera le setter setMaxUploadSize() (documentation du CommonsMultipartResolver). Comme on peut le constater, le setter prend en paramètre un long tandis qu'on lui passe un String< dans le XML. La conversion entre deux types se fait via JavaBeans PropertyEditors.
- la deuxième méthode présente la définition d'une propriété par référence à un bean déjà existant. En occurrence, la propriété dataSource se réfère au bean au même identifiant, défini plus loin dans le code.
<bean id="jpaFlowExecutionListener" class="org.springframework.webflow.persistence.JpaFlowExecutionListener"> <constructor-arg ref="emf" /> <constructor-arg ref="transactionManager" /> </bean>
La présence des balises constructor-arg aide Spring à trouver le constructeur à utiliser. En occurrence, il n'existe qu'un seul constructeur avec des paramètres (Javadoc du JpaFlowExecutionListener). IoC< va alors récupérer les beans emf et transactionManager, et ensuite chercher le constructeur correspondant.
<!-- properties collection --> <beans:property name="javaMailProperties"> <beans:props> <beans:prop key="mail.smtp.auth">true <beans:prop key="mail.smtp.starttls.enable">true </beans:props> </beans:property> <!-- map collection --> <beans:map> <beans:entry key="directory" value="avatars" /> <beans:entry key="prefix" value="av_" /> <beans:entry key="height"><beans:value type="int">40</beans:value></beans:entry> <beans:entry key="width"><beans:value type="int">80</beans:value></beans:entry> <beans:entry key="ratio" value="AUTO" /> <beans:entry key="extension" value="gif" /> </beans:map> <beans:property name="acceptedTags"> <beans:list> <beans:value>b <beans:value>img <beans:value>i <beans:value>em <beans:value>strong </beans:list> </beans:property> <property name="caches"> <set> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/> <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="books"/> </set> </property>
On peut également passer des collections en paramètre. Dans l'exemple ci-dessus, on remarque la définition d'une liste de properties, d'une map, d'une liste et d'un set.
Cycle de vie d'un bean
TODO : fournir un exemple complet d'initialisation d'un bean
Le cycle de vie d'un bean dans Spring peut se résumer à 9 étapes :
- Instanciation : le container trouve la définition d'un bean dans le fichier de configuration et créé une instance grâce à la Java Reflection API
- Définition des propriétés : injection des propriétés du bean
- Définition du nom du bean : définition du nom sous lequel le bean sera connu par le contexte d'application
- Définition de la fabrique du bean : si le bean implémente BeanFactoryAware, la méthode setBeanFactory() est appélée
- Pré-initialisation : si un BeanPostProcessors est défini, la méthode postProcessBeforeInitialization() est appelée avant l'initialisation du bean
- Initialisation : si le bean implémente IntializingBean, la méthode afterPropertySet() est alors appelée. Sinon, c'est la fonction spécifiée dans l'attribut init-method qui est alors invoquée.
- Post-initialisation : si un BeanPostProcessors est défini, la méthode postProcessBeforeInitialization() est appelée après l'initialisation du bean
- Prêt à l'emploi : bean peut être utilisé par l'application
- Destruction : bean est détruit. Par défaut, la fonction destroy() est appélée à ce moment-là. Cependant, une méthode destruction peut être déterminée dans l'attribut destroy-method dans le XML, comme suit :
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> < -- ... -->
Les méthodes d'initialisation et de destruction peuvent être aussi spécifiés au niveau de la balise beans, comme sur cet exemple :
<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" default-init-method="customInit" default-destroy-method="customDestroy">