Participez à la FAQ Spring
Bonjour,
La FAQ Spring est ouverte à tous, vous pouvez donc y participer activement. Soit vous pouvez poster vos questions et réponses dans cette enfilade, soit vous pouvez utiliser l'application collaborative d'édition de FAQ.
L'adresse de la FAQ : http://java.developpez.com/faq/spring/
Merci à tous pour vos contributions.
L'équipe Java
Comment maintenir la session hibernate dans la vue pour utiliser le lazy loading
Si la Session Factory d'hibernate est géré par Spring il est alors possible d'utiliser un filtre mis à disposition par le framework Spring.
Ce filtre commit la transaction après le traitement par la servlet (ou l'action de votre framework préféré), puis recommence une transaction pour la présentation de la jsp ou la techno de vue de votre choix.
Une fois la page rendue le filtre ferme la session. Cette stratégie est aussi utilisé par le framework JBoss Seam.
On configure le filtre dans web.xml :
Code:
1 2 3 4 5 6 7 8 9
|
<filter>
<filter-name>Hibernate Session In View Filter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>Hibernate Session In View Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping> |
Comment accéder aisément au contexte d'application.
L'interface ApplicationContextAware est reconnue par Spring qui y injecte automatiquement le contexte de l'application courante via la méthode setApplicationContext(ApplicationContext context)
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class MyClass implement ApplicationContextAware {
ApplicationContext context;
public void setApplicationContext(ApplicationContext context ){
this.context = context;
}
public AnotherClass getAnotherBean() {
return (AnotherClass) context.getBean("anotherBean");
}
} |
Comment intégrer EhCache et Spring ?
Pour intégrer EhCache et Spring afin de pouvoir injecter directement un bean Cache, on définit le bean ci-dessous dans un des fichiers de définitions du contexte Spring :
Code:
1 2 3 4 5 6 7 8 9 10
| <bean id="customCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager">
<bean class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation"
value="classpath:ehcache.xml" />
<property name="shared" value="false" />
</bean>
</property>
<property name="cacheName" value="SampleConfigOne" />
</bean> |
On place la valeur de la propriété cacheName sur le nom d'une des définitions de cache effecutées dans le fichier ehcache.xml.
Dans cette déclaration, on précise que le fichier de définition des caches se trouve dans le classpath au niveau de la racine (on pourrait également utiliser
classpath*:ehcache.xml pour lui indiquer de chercher ce fichier dans tous les classpath). On précise également que le CacheManager n'est pas partagé via la propriété shared.
Ensuite on peut injecter ce bean normalement dans tout bean qui en aurait besoin via :
Code:
1 2 3
| <bean ...>
<property name="cache" value="customCache" />
</bean> |
Comment intégrer Oracle Toplink et Spring ?
Pour intégrer Oracle Toplink et Spring il faut déclarer un bean "sessionFactory" dans lequel on va préciser :
- L'endroit où se trouve le fichier de session qui contient lui même l'endroit où se trouve le descripteur de mapping
- La source de données
- Le logger
Code:
1 2 3 4 5 6 7
| <bean id="sessionFactory" class="org.springframework.orm.toplink.LocalSessionFactoryBean">
<property name="configLocation" value="toplink-sessions.xml"/>
<property name="dataSource" ref="dataSource"/>
<property name="sessionLog">
<bean class="org.springframework.orm.toplink.support.CommonsLoggingSessionLog"/>
</property>
</bean> |
Dans cet exemple on indique que :
- Le fichier de session Toplink se trouve à la racine du classpath de l'application
- La source de données est un bean déja défini et nommé "dataSource"
- Le logger est CommonsLogging et il est géré par Spring
Voici le contenu de mon fichier de session "toplink-sessions.xml" qui indique que le fichier de mapping "toplink-Mapping.xml" est lui aussi à la racine du classpath de l'application :
Code:
1 2 3 4 5 6 7 8 9
|
<?xml version="1.0" encoding="UTF-8"?>
<toplink-sessions version="10g Release 3 (10.1.3.0.0)" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<session xsi:type="server-session">
<name>Session</name>
<event-listener-classes/>
<primary-project xsi:type="xml">toplink-Mapping.xml</primary-project>
</session>
</toplink-sessions> |
Ensuite pour utiliser ce bean "sessionFactory" il suffit d'hériter de la classe "org.springframework.orm.toplink.support.TopLinkDaoSupport"
et d'utiliser le ToplinkTemplate via "getTopLinkTemplate()" pour executer des traitements.
Exemple de classe :
Code:
1 2 3 4
|
public class MonDaoImpl extends TopLinkDaoSupport implements MonDao {
//Placer les méthodes utilisant "getTopLinkTemplate()" ici...
} |
Configuration de cette classe dans Spring :
Code:
1 2 3 4
|
<bean id="monDao" class="com.drighetto.dao.impl.MonDaoImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean> |
Note : Il faut enlever dans le fichier de mapping Toplink les informations de connexion à la base de données sinon Spring indique qu'il y à un conflit et de ce fait il n'arrive pas à créer une instance du bean "sessionFactory".
Comment accéder au contexte Spring depuis un composant non géré par Spring ?
Pour donner accés au contexte Spring aux composants non gérés par Spring, il suffit d'implémenter un bean géré par Spring qui va exposer des méthodes d'accés au contexte Spring.
Exemple de bean exposant des méthodes d'accés au contexte Spring :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
|
package com.drighetto.essai.springcontextaware;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* Spring Bean Provider - Retrieve a bean in the Spring context and give access
* to Spring context for the bean non managed by Spring
*
* @author Dominique RIGHETTO
*/
public class SpringBeanProvider implements ApplicationContextAware {
/** Spring context */
private static ApplicationContext SPRING_CTX = null;
/**
*
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*
* {@inheritDoc}
*/
public void setApplicationContext(ApplicationContext ctx)
throws BeansException {
SPRING_CTX = ctx;
}
/**
* Method (shortcut) to retrieve a bean from Spring context
*
* @param beanName
* Name of the bean in the context
* @return a object
*/
public static Object getBean(String beanName) {
return SPRING_CTX.getBean(beanName);
}
/**
* Method to access to Spring context
*
* @return a reference to the Spring context
*/
public static ApplicationContext getSpringContext() {
return SPRING_CTX;
}
} |
Remarque : Dans cet exemple on donne accés au contexte en autorisant les actions de modifications sur le contexte Spring et les beans en donnant une référence directe sur ces derniers et non un clone.
Voici la déclaration du bean d'exposition dans la configuration Spring :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
<?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:aop="http://www.springframework.org/schema/aop"
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.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd">
<!-- ============================== BEANS ============================= -->
<!-- Bean provider -->
<bean id="springBeanProvider" class="com.drighetto.essai.springcontextaware.SpringBeanProvider" />
<!-- Simple Spring managed bean used by the example below -->
<bean id="simplePojo"
class="com.drighetto.essai.springcontextaware.SimplePojo">
<property name="message" value="Hello World" />
</bean>
</beans> |
Voici un exemple d'utilisation :
La classe "com.drighetto.essai.springcontextaware.SimplePojo" est une simple classe avec un attribut nommé "message" de type "String" avec des getter/setter...
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
/**
* Entry point
* @param args Command line
*/
public static void main(String[] args) {
try {
// Initialize a Spring context
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// Get a Spring Bean from the bean provider and display value...
SimplePojo simplePojoInstance = (SimplePojo) SpringBeanProvider.getBean("simplePojo");
System.out.printf("Value of POJO message : %s\n", simplePojoInstance.getMessage());
System.out.printf("Number of bean in the context : %s\n",SpringBeanProvider.getSpringContext().getBeanDefinitionCount());
} catch (Exception e) {
e.printStackTrace();
}
} |
La sortie à l'execution de l'exemple est :
Code:
1 2 3
|
Value of POJO message : Hello World
Number of bean in the context : 2 |
Comment intégrer Apache Axis2 et Spring ?
Note : Cette réponse part du principe que le lecteur connaît Axis2 !
Pour intégrer Apache Axis2 et Spring de maniére à ce que ce dernier gére les instances des web services,
il faut déclarer le bean qui représente le web service dans le fichier de configuration du web service, c'est à dire dans le fichier "services.xml" via le tag "SpringBeanName" :
Code:
1 2
|
<parameter name="SpringBeanName" locked="false">monBeanWebService</parameter> |
ET indiquer à Axis que c'est Spring qui est le fournisseur d'instances, cela se fait via le tag "ServiceObjectSupplier" dans le fichier "services.xml"
Voici ces tags dans le fichier "services.xml" :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
| <service name="MonService">
<description>
Mon super service
</description>
<messageReceivers>
<messageReceiver
mep="http://www.w3.org/2004/08/wsdl/in-out"
class="org.apache.axis2.rpc.receivers.RPCMessageReceiver"/>
</messageReceivers>
<parameter name="ServiceObjectSupplier" locked="false">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName" locked="false">monBeanWebService</parameter>
<parameter name="useOriginalwsdl">true</parameter>
</service> |
Donc les lignes importantes dans ce fichier sont :
Code:
1 2 3
|
<parameter name="ServiceObjectSupplier" locked="false">org.apache.axis2.extensions.spring.receivers.SpringServletContextObjectSupplier</parameter>
<parameter name="SpringBeanName" locked="false">monBeanWebService</parameter> |
Car la premiére indique à Axis que c'est Spring qui est le fournisseur d'instances et la seconde quel bean Spring représente le service...
"monBeanWebService" est un bean (une classe publique non abstraite non finale avec des méthodes publiques qui représentent les services exposés) qui est déclaré dans un fichier de configuration Spring.
Comment intégrer JPA et Spring ?
Voici les déclarations à effectuer pour intégrer JPA et Spring (cet exemple se base sur l'utilisation de l'implémentation Oracle Toplink de JPA) :
Etape 1 : Déclaration dans le contexte Spring des dépendances JPA
Activation du tissage lors du runtime pour le contexte Spring afin que tous les beans implémentant l'interface "LoadTimeWeaverAware" (comme le bean LocalContainerEntityManagerFactoryBean) reçoivent une référence vers le tisseur (cf documentation Spring pour plus de précisions)
Code:
1 2
|
<context:load-time-weaver /> |
Déclaration du "PersistenceUnitManager" permettant de personnaliser la sélection des unités de persistences et des sources de données.
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
<bean id="persistenceUnitManager" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<!-- On spécifie ici les lieux où trouver les fichiers de persistence -->
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<!-- On spécifie ici les sources de données à utiliser, locale ou distante -->
<property name="dataSources">
<map>
<entry key="localDataSource" value-ref="dataSource" />
<!--<entry key="remoteDataSource" value-ref="remote-db" />-->
</map>
</property>
<!-- On spécifie ici la sources de données par défaut si aucune source de données n''est disponible -->
<property name="defaultDataSource" ref="dataSource" />
</bean> |
Déclaration de l' "EntityManagerFactory" permettant de fournir les instances des gestionnaires d'entités
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:persistenceUnitManager-ref="persistenceUnitManager">
<!-- On spécifie ici l''adaptateur Spring pour l''implémentation JPA utilisée -->
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter" p:databasePlatform="oracle.toplink.essentials.platform.database.oracle.OraclePlatform"
p:showSql="false" />
</property>
<!-- On spécifie ici le tisseur utilisée pour la modification du ByteCode, cf documentation de Spring pour plus de précisions -->
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<!-- On spécifie ici le dialecte utilisé en fonction de l'' implémentation JPA utilisée -->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaDialect" />
</property>
</bean> |
Déclaration du "TransactionManager" qui est le gestionnaire de transaction
Code:
1 2 3 4 5 6 7 8 9
|
<bean id="txManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory">
<!-- On spécifie ici le dialecte utilisé en fonction de l'' implémentation JPA utilisée -->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.TopLinkJpaDialect" />
</property>
</bean> |
Activation de la prise en compte des annotations de type @Required,@Autowired,@PostConstruct,@PreDestroy,@Resource,@PersistenceContext,@PersistenceUnit
Code:
1 2
|
<context:annotation-config /> |
Déclaration d'un traducteur d'exception
Code:
1 2
|
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" /> |
Etape 2 : Implémentation d'une classe utilisant JPA
Chaque classe qui désire utiliser JPA pour accéder aux données peut :
- Soit hériter de la classe "org.springframework.orm.jpa.support.JpaDaoSupport"
- Soit se faire injecter un attribut de type "org.springframework.orm.jpa.JpaTemplate"
Dans le cas de l'héritage voici le type de déclaration à effectuer pour déclarer la classe (dépendance sur l' "EntityManagerFactory")
Code:
1 2 3
|
<bean id="myDao" class="com.drighetto.springjpa.dao.impl.DaoJpaImpl"
p:entityManagerFactory-ref="entityManagerFactory" /> |
Documentation de Spring sur JPA
Note : Pour le moment l'utilisation de JPA avec Spring ne supporte que l'isolation par défaut pour l'isolation des transactions...
Dans la classe org.springframework.orm.jpa.DefaultJpaDialect (la classe "org.springframework.orm.jpa.vendor.TopLinkJpaDialect" hérite de cette classe) dans la méthode beginTransaction() une vérification est faite sur l'isolation placée et si celle-ci n'est pas placée à défaut alors l'exception suivante est levée "Standard JPA does not support custom isolation levels - use a special JpaDialect for your JPA implementation".
Voici le fichier de contexte dans son ensemble :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
|
<?xml version="1.0" encoding="UTF-8"?>
<!--
********************************************
Application context for the project
********************************************
-->
<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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
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
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd">
<!-- ========================= RESOURCE DEFINITIONS ========================= -->
<!--
Activates a load-time weaver for the context. Any bean within the context that
implements LoadTimeWeaverAware (such as LocalContainerEntityManagerFactoryBean)
will receive a reference to the autodetected load-time weaver.
-->
<context:load-time-weaver />
<!-- DataSource -->
<bean id="dataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
p:driverClassName="oracle.jdbc.driver.OracleDriver"
p:url="jdbc:oracle:thin:@localhost:1521:xe" p:username="MyTestUser"
p:password="MyTestUser" />
<!-- JNDI DataSource for JEE environments -->
<!--
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/petclinic"/>
-->
<!-- JPA PersistenceUnitManager used to customize the selection of the persistence unit and the datasources -->
<bean id="persistenceUnitManager"
class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
<!-- Multiple value can be specified here -->
<property name="persistenceXmlLocations">
<list>
<value>classpath*:META-INF/persistence.xml</value>
</list>
</property>
<property name="dataSources">
<map>
<entry key="localDataSource" value-ref="dataSource" />
<!--<entry key="remoteDataSource" value-ref="remote-db" />-->
</map>
</property>
<!-- if no datasource is specified, use this one -->
<property name="defaultDataSource" ref="dataSource" />
</bean>
<!-- JPA EntityManagerFactory -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
p:dataSource-ref="dataSource"
p:persistenceUnitManager-ref="persistenceUnitManager">
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaVendorAdapter"
p:databasePlatform="oracle.toplink.essentials.platform.database.oracle.OraclePlatform"
p:showSql="false" />
</property>
<property name="loadTimeWeaver">
<bean
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
<property name="jpaDialect">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaDialect" />
</property>
</bean>
<!-- Transaction manager for a single JPA EntityManagerFactory (alternative to JTA) -->
<bean id="txManager"
class="org.springframework.orm.jpa.JpaTransactionManager"
p:entityManagerFactory-ref="entityManagerFactory">
<property name="jpaDialect">
<bean
class="org.springframework.orm.jpa.vendor.TopLinkJpaDialect" />
</property>
</bean>
<!-- ========================= CONFIG DEFINITIONS ========================= -->
<!--
Activates various annotations to be detected in bean classes: Spring's
@Required and @Autowired, as well as JSR 250''s @PostConstruct,
@PreDestroy and @Resource (if available) and JPA's @PersistenceContext
and @PersistenceUnit (if available).
-->
<context:annotation-config />
<!-- Exception translation bean post processor -->
<bean
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
<!-- ================================== TRANSACTIONS DEFINITIONS ================================== -->
<!-- For the moment JPA only support ISOLATION_DEFAULT for the transaction isolation -->
<!-- Define pointcut for txAdvices -->
<aop:config>
<!-- DAO Layer -->
<aop:advisor advice-ref="txAdviceDao"
pointcut="execution(* com.drighetto.springjpa.dao.impl.*.*(..))" />
<!-- Service Layer -->
<aop:advisor advice-ref="txAdviceService"
pointcut="execution(* com.drighetto.springjpa.services.*.*(..))" />
</aop:config>
<!-- the transactional advice for DAO layer -->
<tx:advice id="txAdviceDao" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- Read methods don't use a transaction -->
<tx:method name="read*" propagation="SUPPORTS"
read-only="true" />
<!-- Exclude Getter/Setter -->
<tx:method name="set*" propagation="SUPPORTS"
read-only="true" />
<tx:method name="get*" propagation="SUPPORTS"
read-only="true" />
<!-- All others methods must use a existing transaction -->
<tx:method name="*" isolation="DEFAULT" timeout="10"
propagation="MANDATORY" read-only="false"
rollback-for="org.springframework.dao.DataAccessException" />
</tx:attributes>
</tx:advice>
<!-- the transactional advice for Service layer -->
<tx:advice id="txAdviceService" transaction-manager="txManager">
<!-- the transactional semantics... -->
<tx:attributes>
<!-- Read methods don't use a transaction -->
<tx:method name="display*" propagation="SUPPORTS"
read-only="true" />
<!-- Exclude Getter/Setter -->
<tx:method name="set*" propagation="SUPPORTS"
read-only="true" />
<tx:method name="get*" propagation="SUPPORTS"
read-only="true" />
<!-- All others methods create a transaction -->
<tx:method name="*" isolation="DEFAULT" timeout="10"
propagation="REQUIRES_NEW" read-only="false"
rollback-for="org.springframework.dao.DataAccessException" />
</tx:attributes>
</tx:advice>
<!-- ================================== BEANS DEFINITIONS ================================== -->
<!-- DAO -->
<bean id="myDao" class="com.drighetto.springjpa.dao.impl.DaoJpaImpl"
p:entityManagerFactory-ref="entityManagerFactory" />
<!-- Service -->
<bean id="myService"
class="com.drighetto.springjpa.services.Processor"
p:myDao-ref="myDao" />
</beans> |
Comment fermer un contexte d'application en détruisant les Beans ?
Lors de l'utilisation de Spring en dehors d'un contexte Web, il est intéressant de fermer proprement un contexte d'application, en libérant les resources et les beans.
La méthode close() de l'interface ConfigurableApplicationContext permet de faire cela.
Il faut donc que l'implémentation du contexte d'application Spring implémente cette interface, et heureusement c'est le cas de ClasspathXmlApplicationContext, qui est dans doute la version la plus utilisée.
Code:
1 2 3 4 5
|
ConfigurableApplicationContext context = new ClasspathApplicationContext("com/developpez/hikage/context/applicationContext.xml");
// Termine le contexte Spring
context.close(); |
Attention cependant, seul les beans de scope singleton seront détruits.
Une autre méthode de ConfigurableApplicationContext qui peut s'avérer utile est registerShutdownHook().
Cette méthode permet de spécifier à la JVM de fermer automatiquement le contexte Spring lorsque l'application s'arrête.
Comment utiliser JRuby avec Spring ?
L'objectif recherché est d'utiliser dans une application Java/JEE des implémentations JRuby de certains interfaces définis en java.
Deux approches sont possibles :
- Soit l'implémentation JRuby se trouve dans le fichier de configuration Spring
- Soit l'implémentation JRuby se trouve dans un fichier de script
Pour expliquer les deux cas nous allons configurer dans Spring les implémentations de l'interface com.drighetto.springjruby.ActionDefinition dont le code est ci-dessous :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
package com.drighetto.springjruby;
import java.io.IOException;
/**
* Interface defining some actions
*
* @author Dominique RIGHETTO <dominique.righetto@gmail.com>
*
*/
public interface ActionDefinition {
/**
* Method to write content to a file
*
* @param content
* Content
* @param filename
* Name of the target file with the full path
* @throws IOException
*/
void writeContent(String content, String filename) throws IOException;
/**
* Method to read the content to a file
*
* @param filename
* Name of the target file with the full path
* @return The file content as a String
* @throws IOException
*/
String readContent(String filename) throws IOException;
/**
* Method to obtain the last method called
*
* @return a message
*/
String obtainLastMethodCalled();
} |
L'espace de nommage utilisé ici est "lang", ce dernier nécessite la déclaration suivante dans les fichiers de configuration Spring :
Code:
1 2 3 4 5 6 7 8 9 10
|
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-2.5.xsd">
...
</beans> |
Cas 1 : l'implémentation JRuby se trouve dans le fichier de configuration Spring
Voici la configuration Spring associée :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
<lang:jruby id="actionDefinitionInline" script-interfaces="com.drighetto.springjruby.ActionDefinition">
<lang:inline-script>
#Needed to use java
require 'java'
#JRuby implementation of the "com.drighetto.springjruby.ActionDefinition" Java interface
class ActionDefinitionJRubyImpl
#Indicate that this JRuby class implement the "com.drighetto.springjruby.ActionDefinition" Java interface
include com.drighetto.springjruby.ActionDefinition
#Define instance attribute accessor
attr_reader :last_called_method
#Constructor
def initialize()
@last_called_method = ""
end
#Explicit Java Style Setter method required by Spring !!!
def setLast_called_method(text)
@last_called_method = text
end
#Implementation of the writeContent() method
def writeContent(content, filename)
my_file = File.new(filename, "w")
my_file.puts content
my_file.close
@last_called_method = "writeContent() on #{filename}"
end
#Implementation of the readContent() method
def readContent(filename)
content = ""
my_file = File.new(filename, "r")
while (line = my_file.gets)
content = content + line
end
my_file.close
@last_called_method = "readContent() on #{filename}"
return content
end
#Implementation of the obtainLastMethodCalled() method
def obtainLastMethodCalled()
return @last_called_method
end
end
</lang:inline-script>
<lang:property name="last_called_method" value="No method called" />
</lang:jruby> |
Voici la description des attributs du tag "lang:jruby" :
- id : Nom du bean Spring
- script-interfaces : Nom complet de l'interface implémenté
Le tag "lang:inline-script" sert à placer le corps du script
Le tag "lang:property" sert à injecter des valeurs dans le script JRuby
Cas 2 : l'implémentation JRuby se trouve dans un fichier de script
Voici la configuration Spring associée :
Code:
1 2 3 4 5 6
|
<lang:jruby id="actionDefinition" refresh-check-delay="5000" scope="prototype"
script-interfaces="com.drighetto.springjruby.ActionDefinition"
script-source="classpath:/jruby/action_definition_jruby_impl.rb">
<lang:property name="last_called_method" value="No method called" />
</lang:jruby> |
Voici la description des attributs du tag "lang:jruby" :
- id : Nom du bean Spring
- refresh-check-delay : Delai entre lequel Spring va vérifier si le script à été modifié, attention cette vérification ne se fait que lorsque le bean est utilisé, ce n'est pas une tâche récurrente planifiée...
- scope : Portée du bean (singleton ou prototype)
- script-source: Chemin vers le fichier de script, il est possible d'utiliser les spécifications de chemins offertes par Spring pour l'accés aux ressources (filesystem, classpath,...)
- script-interfaces : Nom complet de l'interface implémenté
Le tag "lang:property" sert à injecter des valeurs dans le script JRuby
Voici le contenu du script "action_definition_jruby_impl.rb"
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
|
#Needed to use java
require 'java'
#JRuby implementation of the "com.drighetto.springjruby.ActionDefinition" Java interface
class ActionDefinitionJRubyImpl
#Indicate that this JRuby class implement the "com.drighetto.springjruby.ActionDefinition" Java interface
#-> Optional in Spring : If is not specified Spring will use the interface defined in the XML config declaration
#-> Mandatory in pure JRuby
#include com.drighetto.springjruby.ActionDefinition
#Define instance attribute accessor
attr_reader :last_called_method
#Constructor
def initialize()
@last_called_method = ""
end
#Explicit Java Style Setter method required by Spring !!!
def setLast_called_method(text)
@last_called_method = text
end
#Implementation of the writeContent() method
def writeContent(content, filename)
my_file = File.new(filename, "w")
my_file.puts content
my_file.close
@last_called_method = "writeContent() on #{filename}"
end
#Implementation of the readContent() method
def readContent(filename)
content = ""
my_file = File.new(filename, "r")
while (line = my_file.gets)
content = content + line
end
my_file.close
@last_called_method = "readContent() on #{filename}"
return content
end
#Implementation of the obtainLastMethodCalled() method
def obtainLastMethodCalled()
return @last_called_method
end
end
#Instantiate and return a new instance of the ActionDefinitionJRubyImpl class
#Note from the Spring documentation :
# If you forget to do this, it is not the end of the world; this will however
#result in Spring having to trawl (reflectively) through the type representation of
#your JRuby class looking for a class to instantiate.
# In the grand scheme of things this will be so fast that you'll never notice it, but
#it is something that can be avoided by simply having a line such as the one above as
#the last line of your JRuby script. If you don't supply such a line, or if Spring cannot
#find a JRuby class in your script to instantiate then an opaque ScriptCompilationException
#will be thrown immediately after the source is executed by the JRuby interpreter.
ActionDefinitionJRubyImpl.new |
Ensuite aprés avoir configuré le bean on peut l'utiliser de maniére transparente dans un autre bean ou dans une classe via l'interface définie en java.
Code:
1 2 3 4 5 6
|
//Récupération de l'instance du bean
ActionDefinition actionDefinition = (ActionDefinition) applicationContext.getBean("actionDefinition");
//Utilisation normale
actionDefinition.writeContent(.....); |
Rubrique dédiée dans la documentation Spring
Cet exemple ayant été réalisé avec Maven2, voici la liste des dépendances :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.9</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.1_3</version>
</dependency>
</dependencies> |
Puis-je utiliser Spring gratuitement dans mon projet ?
Oui
Spring Framework ainsi que la plupart des projets du portfolio Spring sont distribués sous licence Apache 2.0.
L'utilisation de ces projets au sein d'un développement est donc complètement libre et gratuite.
Comment injecter des dépendances dans une servlet ?
Depuis Spring 2.5.1, une nouvelle classe utilitaire est disponible : SpringBeanAutowiringSupport
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
public class MaServlet extends HttpServlet {
//Spring 3.0
@Inject
private MonService service;
@Autowired
private MonService service2;
@Resource
private MonService service3;
@Override
public void init() {
SpringBeanAutowiringSupport.
processInjectionBasedOnCurrentContext(this);
}
} |
Résoudre le problème "No Hibernate Session bound to thread"
Le problème vient du fait qu'Hibernate a besoin d'effectuer les opérations que vous lui demandez dans une transaction.
D'habitude, tout se fait de manière automatique pour peu que vous ayez bien configuré votre fichier application-context.xml en y indiquant un transactionManager et un proxyTransactionnel (cf de nombreux tutoriels que l'on peut trouver sur google).
TRADUCTION D'UN FORUM ANGLOPHONE : DEBUT
D'un autre coté, il se peut que vous ayez besoin d'exécuter un bout de code hors du proxy transactionnel, et dans ce cas la méthode habituelle suivante ne marche plus:
Code:
1 2 3 4 5 6 7 8 9
| Session session = sessionFactory.getCurrentSession();
try {
session.beginTransaction();
// do stuff...
session.getTransaction().commit();
catch(Exception ex) {
session.getTransaction().rollback();
} |
En effet elle ne peut être utilisée lorsque la SessionFactory est configurée via LocalSessionFactoryBean. A la place il faut s'arranger pour que Spring gère lui même les problèmes de transaction.
Soit en xml:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!-- define which methods -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="create" />
<tx:method name="update" />
<tx:method name="delete" />
<tx:method name="find" read-only="true" />
</tx:attributes>
</tx:advice>
<!-- define which classes -->
<aop:config>
<aop:pointcut id="serviceOperation" expression="execution(* my.services.*Service.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperation" />
</aop:config>
<!-- this bean will have automatic transaction management -->
<bean id="foobar" class="my.services.FooBarService" /> |
soit au sein du code:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| ApplicationContext appContext = new ClassPathXmlApplicationContext("applicationContext.xml");
PlatformTransactionManager txManager = (PlatformTransactionManager) appContext.getBean("transactionManager");
TransactionTemplate txTemplate = new TransactionTemplate(txManager);
txTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
try {
// do stuff here...
} finally {
status.setRollbackOnly();
}
return null;
}
}); |
TRADUCTION D'UN FORUM ANGLOPHONE : FIN
Commentaire: faire les choses au sein du code est hideux, car on instancie des classes de Spring, ce qui est contraire à la philosophie de Spring.
En ce qui me concerne, j'ai abouti à une solution à mi chemin: via application.xml, j'injecte le transactionManager défini dans spring dans ma classe java. Puis à partir de là je fais le reste dans le code.
SOURCE
La SessionFactory Par annotation ou fichiers properties Testez et tenez moi au courant merci!
Bonjour, Peut-on avoir une intégration de Spring dans Hibernate sans utiliser les fichiers de configurations des 2 frameworks? mais soir en utilsant les fichiers properties soit une annotation? Pourquoi Spring n'a pas pensé à créer une simple annotation pour dans son intégration à Hibernate puis une classe créant l'insatnce d'une SessionFactory, ce qui permet de rendre l'utilisation d'une couche Dao sans configurer la SessionFactory dans le contexte? J'ai une solution. Elle consiste à créer une classe créant l'instance de la SessionFactory puis soit utiliser des fichiers properties soit utiliser une annotation. Merci de la tester et me donner votre avis:
La classe qui permet de créer la SessionFactory est la suivante:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
public class SWESessionFactory extends AnnotationSessionFactoryBean{
private static final Log log = LogFactory.getLog(SWESessionFactory.class);
/*Le nom du fichier de properties contenant les classes*/
private String annotatedClassesPropertiesFile="annotatedClasses.properties";
/*Nom du fichier properties pour la connexion en base de données*/
private String dataBasePropertiesFile="databaseProperties.properties";
/*Nom du fichier properties pour les propriétés d'hibernate*/
private String hibernatePropertiesFile="hibernateProperties.properties";
/**
*
*/
public SWESessionFactory() {
// TODO Auto-generated constructor stub
super();
}
/**
*
* @param byProperties : si vrai alors la configuration doit être faite par
* fichiers properties, faux on utilisation l'annotation SWEFactory
* @return une instance de la session Factory
*/
public final SessionFactory configure(boolean byProperties){
if(byProperties)return propertiesConfiguration();
return annotatedConfiguration();
}
/**
*
* @return une instance de la session factory prêt à l'emploi
* avec une configuration avec des fichiers properties
*/
@SuppressWarnings("rawtypes")
private final SessionFactory propertiesConfiguration(){
log.debug("Initialisation de la sessionFactory par les fichiers de Properties");
List<Class> classes = null;
Class[] annotatedClasses;
classes=loadClasses();
annotatedClasses=classes.toArray(new Class[classes.size()]);
setAnnotatedClasses(annotatedClasses);
setHibernateProperties(loadPropertiesFile(hibernatePropertiesFile));
setDataSource(loadDataSource(null));
try {
log.debug("Chargement de la session Factory");
return buildSessionFactory();
} catch (Exception e) {
// TODO Auto-generated catch block
log.error("Le chargement de la session Factory a échoué");
throw new SWEException("Impossible de localiser la SessionFactory",e);
}
}
/**
*
* @return une instance de la session factory prêt à l'emploi
* Dans ce cas on impose à la classe Dao d'être annotée
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
private final SessionFactory annotatedConfiguration(){
log.debug("Initialisation de la session Factory avec les annotations");
StackTraceElement stes[]=Thread.currentThread().getStackTrace();
SWEFactory swef=null;
for(StackTraceElement ste:stes){
String className=ste.getClassName();
Class c=null;
try {
log.debug("Détermination de la classe courrente");
c=getClass().getClassLoader().loadClass(className);
} catch (ClassNotFoundException e) {
log.debug("On ne passe pas ici");
e.printStackTrace();
}
if(c.isAnnotationPresent(SWEFactory.class)){
swef=(SWEFactory) c.getAnnotation(SWEFactory.class);
log.debug("La classe courrente a été trouvée il déclare bien l'annotation SWEFactory :"+swef);
break;
}
}
setAnnotatedClasses(swef.annotatedClasses());
Properties p=new Properties();
PropertyDataSource pdss[]=swef.dataSource();
for(PropertyDataSource pds:pdss)p.setProperty(pds.key().name(), pds.value());
setDataSource(loadDataSource(p));
p.clear();
HibernateProperties hps[]=swef.hibernateProperties();
for(HibernateProperties hp:hps) p.setProperty(hp.key(), hp.value());
setHibernateProperties(p);
try {
log.debug("Chargement de la session Factory");
return buildSessionFactory();
} catch (Exception e) {
log.error("Le chargement de la session Factory a échoué");
throw new SWEException("Impossible de localiser la SessionFactory",e);
}
}
/**
*
* @param annotatedClassesPropertiesFile: préciser le nom du fichier properties pour les classes
*/
public void setAnnotatedPropertiesFile(String annotatedClassesPropertiesFile){
this.annotatedClassesPropertiesFile=annotatedClassesPropertiesFile;
}
/**
*
* @param dataBasePropertiesFile: préciser le nom du fichier properties pour la dataBaseProperties
*/
public void setDataBasePropertiesFile(String dataBasePropertiesFile){
this.dataBasePropertiesFile=dataBasePropertiesFile;
}
/**
*
* @param hibernatePropertiesFile: préciser le nom du fichier properties hibernate
*/
public void setHibernatePropertiesFile(String hibernatePropertiesFile){
this.hibernatePropertiesFile=hibernatePropertiesFile;
}
/**
*
* @return la liste des classes annotées dans le fichier properties
*/
@SuppressWarnings("rawtypes")
private final List<Class> loadClasses(){
List<Class> classes=new ArrayList<Class>();
Properties p=null;
log.debug("Localisons du fichier de properties "+annotatedClassesPropertiesFile);
p = loadPropertiesFile(annotatedClassesPropertiesFile);
Set<Entry<Object,Object>> set=p.entrySet();
Iterator<Entry<Object,Object>> it=set.iterator();
while(it.hasNext()){
Entry<Object,Object> en=it.next();
Class<?> c;
String cls=en.getValue().toString();
try {
log.debug("Mise à jour de la classe "+cls);
c = Class.forName(cls);
} catch (ClassNotFoundException e) {
log.error("La classe "+cls+" n'existe pas");
throw new SWEException("La classe "+cls+" n'existe pas",e);
}
classes.add(c);
}
return classes;
}
/**
* @param p: contient les propriétés de la base de données qui peut provenir soit
* du fichier properties sot de l'annotation
* @return l'instance de la DataSource qui nous permettra d'établir une connexion
* à la base de données
*/
private final DriverManagerDataSource loadDataSource(Properties p){
DriverManagerDataSource dataSource=new DriverManagerDataSource();
p=p==null?loadPropertiesFile(dataBasePropertiesFile):p;
dataSource.setDriverClassName(p.getProperty("driverClassName"));
dataSource.setPassword(p.getProperty("password"));
dataSource.setUrl(p.getProperty("url"));
dataSource.setUsername(p.getProperty("username"));
dataSource.setConnectionProperties(p);
return dataSource;
}
private final Properties loadPropertiesFile(String name){
InputStream in=getClass().getClassLoader().getResourceAsStream(name);
Properties p=new Properties();
try {
p.load(in);
} catch (IOException e) {
e.printStackTrace();
}
return p;
}
} |
Cette classe peut être utilisée soit par des fichiers properties soit par une annotation dont le code est le suivant:
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
@Retention(RetentionPolicy.RUNTIME)
@Target ({ElementType.TYPE})
@Inherited
public @interface SWEFactory {
@SuppressWarnings("rawtypes")
public Class[] annotatedClasses();
public PropertyDataSource[] dataSource();
public HibernateProperties[] hibernateProperties();
public @interface PropertyDataSource{
public enum KEY {url,username,password,driverClassName};
public KEY key();
public String value();
}
public @interface HibernateProperties{
public String key();
public String value();
} |
Testez le sur une couche Dao et tenez moi au courant
Merci!
Comment externaliser des propriétés dans un fichier Properties ?
Je voudrais juste ajouter une petite précision à cette FAQ.
Il faudrait simplement préciser que cela fonctionne si on charge le contexte Spring dans un ApplicationContext. Ca ne fonctionne pas si on utilise directement une BeanFactory. Cette précision pourrait être utile pour les débutants à mon sens.