Explorer les Livres électroniques
Catégories
Explorer les Livres audio
Catégories
Explorer les Magazines
Catégories
Explorer les Documents
Catégories
FRAMEWORK SPRING
Octobre 2020
SOMMAIRE
1. Introduction à Spring..................................................................................................................................5
Introduction....................................................................................................................................................................................................................5
1
Formation : Développement JEE avec Spring
Pré-requis........................................................................................................................................................................................................................5
1.1 Historique de Spring..........................................................................................................................................................................................5
1.2 Spring et J2EE.....................................................................................................................................................................................................7
1.3 Les modules Spring.............................................................................................................................................................................................8
1.4 Concept de Beans...............................................................................................................................................................................................9
1.5 Le contexte d'application Spring...................................................................................................................................................................9
1.6 Conception de Beans........................................................................................................................................................................................10
1.6.1 Déclarer des beans...................................................................................................................................................................................10
1.6.2 Configurer le contexte.............................................................................................................................................................................11
1.6.3 Chargement d’un contexte.....................................................................................................................................................................11
1.6.4 Récupération d’une référence d’un Bean...........................................................................................................................................11
1.6.5 Constructeurs.............................................................................................................................................................................................12
1.6.6 Configuration de bean dans le cas de l’héritage.............................................................................................................................13
1.6.8 Injection de dépendances.......................................................................................................................................................................15
1.6.9 Chargement et utilisation de propriétés dans un bean..................................................................................................................23
1.6.10 Portée des beans (scoping).................................................................................................................................................................23
1.6.11 Les espaces de nommage.....................................................................................................................................................................26
1.6.12 Initialisation et destruction de beans.............................................................................................................................................28
1.6.13 Injection dans les propriétés des beans.........................................................................................................................................28
1.6.14 Référencer d’autres beans.................................................................................................................................................................28
1.6.15 La liaison automatique inter beans (Auto-wiring).......................................................................................................................28
2 Accès aux bases de donnés.........................................................................................................................31
2.1 Accès par JDBC..................................................................................................................................................................................................31
b) Dépendances du projet...................................................................................................................................................................................31
2.2 Accès par JdbcTemplate................................................................................................................................................................................38
2.3 Intégration de Spring avec Hibernate.......................................................................................................................................................39
3 Gestion des transactions............................................................................................................................43
3.1 Tour d'horizon des gestionnaires de transactions indépendants...............................................................................................................43
3.2 Implémentation d'un scénario "Two phase-commit" avec Atomikos.............................................................................................................44
b) Configuration et codage de l'application........................................................................................................................................................48
Les tests.......................................................................................................................................................................................................................55
Classe de Services........................................................................................................................................................................................................55
Transactions entre 2 bases de données........................................................................................................................................................................58
2
Formation : Développement JEE avec Spring
3
Formation : Développement JEE avec Spring
1. Introduction à Spring
Introduction
Spring est un framework Java écrit par un groupe de programmeurs chevronnés, afin de simplifier le processus de développement des applications d’entreprise.
Ce framework peut être vu comme une boite à outils, constituée d’un ensemble d’API, qui offre des moyens de:
- Concevoir une application basée sur POJO (plain old Java objects). Remarque : Un POJO est une classe Bean Java
- Réduire le couplage grace à l’injection de dépendences et une conception applicative orientée interface
- Utilisation de la programmation déclarative
- Réduction des temps de développement par l’utilsiation des templates
Pré-requis
Afin de d’utiliser le framework Spring, il est indispensable d’avoir des connaissances de base du langage de programmation Java, et idéalement maitriser un IDE tel que
Eclipse.
Spring 1.2
support de JMX
support JDO 2, Hibernate 3, TopLink
support de JCA CCI, JDBC Rowset
déclaration des transactions avec @Transactional
Spring 2.0 apporte de nombreuses nouveautés :
le support et l'utilisation d'AspectJ
la configuration XML basée sur un schéma XML
des simplifications de la configuration notamment avec des namespaces dédiés (beans, tx, aop, lang, util, jee, p)
les Message Driven POJO
les annotations @Repository, @Configurable
Spring 2.5 apporte de nombreuses nouveautés pour faciliter sa configuration :
l'ajout de nouveaux namespaces (context, jms) avec de nouveaux tags
l'enrichissement des namespaces existants (jee, aop)
l'ajout d'annotations concernant le cycle de vie des beans (@Service, @Component, @Controller), autowiring (@Autowired, @Qualifier, @Required), la gestion des
transactions (@Transactional) et support des annotations standards de Java 5 (@PostConstruct, @PreDestroy, @Resource)
les tests d'intégration reposant sur Junit 4 et des annotations (@ContextConfiguration, @TestExecutionListeners, @BeforeTransaction, @AfterTransaction)
Spring 3.0 apporte de nombreuses nouveautés pour sa configuration et les fonctionnalités proposées :
des possibilités enrichies de configurer le context en utilisant des annotations : annotations issues du projet Spring JavaConfig qui sont ajoutées dans Spring Core
(@Configuration, @Bean, @DependsOn, @Primary, @Lazy, @Import, @ImportResource et @Value)
Spring Expression Langage (SpEL) : un langage d'expressions utilisable pour la définition des beans dans Spring Core et pour certaines fonctionnalités dans des projets du
portfolio
le support de REST
Object to XML Mapping (OXM) : abstraction pour utiliser des solutions de mapping objet/XML initialement proposée par le projet Spring Web services et intégrée dans
Spring Core
requiert un Java SE 5.0 ou supérieur (refactoring des API pour une utilisation des generics, des varargs, de java.util.concurrent, ...)
une nouvelle modularisation : la distribution de Spring en jar a été revue pour que chaque module ait son propre jar. L'archive spring.jar n'est plus proposée
le support de moteurs de bases de données embarquées (Derby, HSQL, H2)
5
Formation : Développement JEE avec Spring
6
Formation : Développement JEE avec Spring
d) WEB et REMOTING
Le paradigme Model-View-Controller (MVC) est l’approche la plus communément utilisée pour la conception d’application web, ce qui garantit une stricte separation entre
l’interface utilisateur et la logique applicative. Il existe en Java de nombreux frameworks MVC, tells que Apache Struts, JSF, WebWork, etTapestry (qui implémentent tous
l’approche MVC).
Spring implémente une couche web spécifique, à travers un framework basé sur une servlet (pour les applications web conventionnelles/classiques) ; et un autre pour le
développement d’application portels (en s’appuyant sur l’API Java Portlet).
En plus des applications web (partie interface utilisateur), ce module offre des solutions pour les traitements distants, afin d’interagir avec d’autres applications. Les
capacités de Spring en termes de remoting (traitements distants) incluent Remote Method Invocation (RMI), Hessian, Burlap, JAX-WS, et l’invocateur HTTP spécifique à
Spring’.
7
Formation : Développement JEE avec Spring
e) Tests
Compte-tenu de l’importance des tests écrits par le développeur, Spring a prévu un module dédié aux tests (JNDI, servlets, and portlets). Au niveau de la phase des tests
dintégration, ce module fournit une prise en charage du chargement de beans dans le contexte d’application Spring.
8
Formation : Développement JEE avec Spring
Dans une application basée sur Spring, les objets sont gérés par le conteneur Spring. Ce conteneur prend en charge tous les aspects liés aux différents objets ( beans), à leurs
dépendances, à leur configuration respective, et la gestion de leur cycle de vie.
Dans Spring, cette gestion des beans est réalisée avec la notion de contexte d’application (Application context). Le contexte d’application permet à Spring de créer,
configurer et lier les différents objets (ou beans), en utilisant l’injection de dépendances, pour lier les différents composants d’une application.
L’avantage de cette approche est de simplifier la compréhension d’une application, de garantir une meilleure réutilisation, et permettre les tests unitaires.
a) Interface du bean
package org.formation.spring;
import org.formation.spring.exception.ComtabiliteException;
package alti.formation.spring;
public class TraitementComptableImpl implements TraitementComptable
{
public void enregistrerPiece(int mois, int exercice, String numero) throws ComptabiliteException
{
// implémenter la méthode
}
9
Formation : Développement JEE avec Spring
La definition d’un bean nécessitera que l’on insére une balise <bean> dans le bloc parent <beans>
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
10
Formation : Développement JEE avec Spring
Classe GestionComptabilite :
package alti.formation.spring;
Classe GestionReferentiel :
package alti.formation.spring;
<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-2.5.xsd">
<bean id="premierBean" class="alti.formation.spring.GestionComptabilite ">
</bean>
1.6.5 Constructeurs
Il est possible d’intervenir dans un fichier de configuration de Spring pour renseigner des valeurs qui seront utilisées lors de l’instanciation du bean.
Exemple:
Exemple:
package alti.formation.spring;
public class Client
{
private int type;
private String action;
private String pays;
// méthodes getters et setters pour chaque attribut
}
<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-2.5.xsd">
12
Formation : Développement JEE avec Spring
<bean id="clientAlgerieBean" class="alti.formation.spring.Client">
<property name="pays" value="Algerie" />
</bean>
<bean id="clientBean" parent="clientAlgerieBean ">
<property name="action" value="achat" />
<property name="type" value="1" />
</bean>
</beans>
Remarques:
Dans l’exemple ci-dessus, le bean clientBean contient l’initialisation des proprieties type et action, qui sont héritées du bean clientAlgerieBean.
package alti.formation.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Client client = (Client)context.getBean("clientBean");
System.out.println(cust);
}
}
Remarque: Le bean clientBean hérite la propriété pays de son parent (bean clientAlgerieBean).
13
Formation : Développement JEE avec Spring
Il est possible de definir dans un contexte d’application un heritage entre beans, en utilisant une classe abstraite comme classe de base. 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-2.5.xsd">
<bean id="ClientAbstraitBean" class="alti.formation.spring.ClientAbstrait" abstract="true">
<property name="pays" value="Algérie" />
</bean>
<bean id="ClientBean" parent="ClientAbstraitBean" class="alti.formation.spring.Client">
<property name="action" value="achat" />
<property name="type" value="1" />
<property name="pays" value="Tunisie" />
</bean>
</beans>
Commentaire:
Le bean ClientBean utilise la redéfinition (override) du bean parent abstrait ClientAbstraitBean
Une fois que le bean Calcul est instancié par Spring, ce dernier utilise la méthode setter pour injecter la valeur test1 à la propriété nom du bean.
Remarque :
L’injection de valeurs de propriétés ne se limite pas au chaines de caractères (String) mais peut aussi concernes des types (boolean, int, float, java.lang.Double, etc..) .
Remarque:
En fonction du type de la propriété, Spring effectuera la conversion adéquate.
b) Injection de références
Dans la definition d’un contexte d’application, il est possible de definer l’injection de references. L’exemple ci-dessous illustre la méthode à utiliser:
15
Formation : Développement JEE avec Spring
Remarques:
16
Formation : Développement JEE avec Spring
D’un point de vue conceptuel, l’utilisation d’une référence vers une interface dans la classe Composant permet de réduire le couplage, et de faciliter ainsi toute
modification ultérieure, pour peu que l’on fournisse une instance d’une classe qui implémente l’interface Reporting. Spring encourage fortement l’utilisation de ce principe
dans le développement et les tests des applications.
Le code Java qui permet de mettre en relief l’intérêt de cette approche est le suivant :
package alti.formation.spring;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
17
Formation : Développement JEE avec Spring
public class Entreprise
{
private List<Object> collaborateurs;
private Set<Object> cadres;
private Map<Object, Object> dirigeants;
private Properties contacts;
. . .
}
package alti.formation.spring;
public class Personne
{
private int age ;
private String nom;
private String adresse;
. . .
}
19
Formation : Développement JEE avec Spring
</property>
</bean>
<bean id="PersonneBean" class="alti.formation.spring.Personne">
<property name="nom" value="nom1" />
<property name="adresse" value="adresse1" />
<property name="age" value="50" />
</bean>
</beans>
20
Formation : Développement JEE avec Spring
package alti.formation.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Application
{
public static void main( String[] args )
{
ApplicationContext context = new ClassPathXmlApplicationContext("contexte.xml");
Entreprise entreprise = (Entreprise) context.getBean("EntrepriseBean");
System.out.println(cust);
}
}
Client [
collaborateurs=[
1,
Personne [adresse=adresse1, age=35, nom=untel1],
],
dirigeants={
key 1=1,
key 2=Personne [adresse=adresse1, age=50, nom=nom1],
key 3=Personne [adresse=adresse3, age=58, nom=untel3]
},
contacts{admin=admin@nospam.com, support=support@nospam.com},
cadres=[
1,
Personne [adresse=adresse2, age=40, nom=untel2],
]
21
Formation : Développement JEE avec Spring
d)
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:parametres_applicatifs.properties"/>
</bean>
a) Scope Singleton
Exemple d’un bean singleton
22
Formation : Développement JEE avec Spring
<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-2.5.xsd">
<bean id="clientBean" class="alti.formation.spring.Client" />
</beans>
package alti.formation.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
Etant donné que le bean ‘clientBean’ est défini dans une portée de type singleton, la seconde référence pointera la même référence.
23
Formation : Développement JEE avec Spring
b) Scope Prototype
Par opposition au scope singleton, le scope prototype va renvoyer une instance différente par appel.
Le fichier de définition du contexte d’application Spring sera :
<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-2.5.xsd">
<bean id="clientService" class="alti.formation.spring.ClientService" scope="prototype"/>
</beans>
L’exécution de l’exemple précédent avec la nouvelle définition du contexte d’application Spring donnera alors:
Message de l’instance client1 : contenu client1
Message de l’instance client2 : null
package alti.formation.spring.ClientService;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
@Service
@Scope("prototype")
public class ClientService
{
String message;
public String getMessage() {
return message;
24
Formation : Développement JEE avec Spring
}
public void setMessage(String message) {
this.message = message;
}
}
Il faut ensuite modifier l’application contexte de Spring (fichier xml) de manière à y inclure le nom du package pour lequel les annotations seront traitées.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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">
<context:component-scan base-package="alti.formation.spring" />
</beans>
25
Formation : Développement JEE avec Spring
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
<!-- ... -->
</beans>
Le tag <beans> est l'élément racine du fichier de configuration du context Spring. Le tag <beans> peut avoir plusieurs tags fils : <alias>, <bean>, <description> et <import>.
Le tag <bean> permet de configurer un bean. Il possède plusieurs attributs :
Attribut Rôle
abstract Booléen qui précise si le bean est abstrait : la valeur true indique au conteneur de ne pas créer d'instance
autowire Permet de préciser comment le bean sera injecté : byType, byName, constructor, autodetect, none (pas d'autowiring)
autowire-candidate Booléen qui précise si l'instance du bean peut être utilisé lors de l'injection de dépendances
dependency-check Préciser les dépendances qui seront valorisées par le conteneur : simple (pour les primitives), object (pour les objets), default, none, all
depends-on Préciser un bean qui devra être initialisé avant que le bean soit instancié
destroy-method Préciser une méthode qui sera invoquée lorsque le bean est déchargé du conteneur
factory-bean Préciser un bean dont la méthode précisée par l'attribut factory-method sera utilisée comme fabrique
factory-method Préciser une méthode statique du bean précisé par l'attribut factory-bean qui sera utilisée par le conteneur comme une fabrique
id Identifiant du bean
init-method Nom de la méthode d'initialisation qui sera invoquée une fois l'instance créée et les dépendances injectées
Le tag <bean> peut avoir plusieurs tags fils : <constructor-arg>, <description>,<lookup-method>,<meta>,<property> et <replaced-method>.
Le tag <constructor-arg> permet d'utiliser l'injection par constructeur : il permet de fournir une valeur ou une référence sur un bean géré par le conteneur
Le tag <lookup-method> permet d'utiliser l'injection par getter : le getter est remplacé par une autre implémentation qui retourne une instance particulière.
26
Formation : Développement JEE avec Spring
Le tag <property> permet d'utiliser l'injection par setter pour fournir une valeur à une propriété. Cette valeur peut être une référence sur un autre bean géré par le conteneur.
Le tag <replaced-method> permet de remplacer les traitements d'une méthode du bean par une autre implémentation.
Le tag <alias> permet de définir un alias pour un bean.
Le tag <import> permet d'importer une autre partie de la définition du contexte de Spring. Ce tag est particulièrement utile pour définir le contexte dans plusieurs fichiers,
chacun contenant des définitions de beans par thème fonctionnel ou technique (services, transactions, accès aux données, ...)
Le tag <description> permet de fournir une description de la définition du contexte ou d'un bean.
Exemple :
<bean id="client" class="alti.formation.spring.Client" autowire="byName" />
27
Formation : Développement JEE avec Spring
package alti.formation.spring;
public class Client
{
private Personne personne;
public Client(Personne personne) {
this.personne = personne;
}
public void setPersonne(Personne personne) {
this.personne = personne;
}
//...
}
28
Formation : Développement JEE avec Spring
package alti.formation.spring;
public class Personne
{
//...
}
29
Formation : Développement JEE avec Spring
<bean id="personne" class="alti.formation.spring.Personne" />
5. Auto-Wiring ‘autodetect’
Il existe le mode d’injection par auto-détection. Dans ce mode, s’il existe un constrcteur par défaut, il utilisera l’injection par contructeur, ou le mode par type.
Exemple d’un contexte d’application Spring qui utilise le mode auto détection :
<bean id="client" class="alti.spring.formation.Client" autowire="autodetect" />
<bean id="personne" class="alti.formation.spring.Personne"" />
use gestion;
b) Dépendances du projet
30
Formation : Développement JEE avec Spring
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mkyong.common</groupId>
<artifactId>SpringExample</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>SpringExample</name>
<url>http://maven.apache.org</url>
<dependencies>
<!-- Spring framework -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<!-- MySQL database driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.9</version>
</dependency>
</dependencies>
</project>
c) Classe du modèle
package alti.formation.spring.modele;
import java.sql.Timestamp;
public class Client
31
Formation : Développement JEE avec Spring
{
private int id;
private String nom;
private int age;
// rajouter les méthodes getter et setter
}
package alti.formation.spring.dao;
import alti.formation.spring.modele.Client;
public interface ClientDAO
{
public void inserer(Client client);
public Customer chercherParId(int id);
}
32
Formation : Développement JEE avec Spring
package alti.formation.spring.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.sql.DataSource;
import alti.formation.spring.dao.ClientDAO;
import alti.formation.spring.modele.Client;
public class JdbcClientDAO implements ClientDAO
{
private DataSource dataSource;
public void setDataSource(DataSource dataSource)
{
this.dataSource = dataSource;
}
public void inserer(Client client){
String sql = "INSERT INTO CLIENT " +
"(ID, NAME, AGE) VALUES (?, ?, ?)";
Connection cnt = null;
try {
cnt = dataSource.getConnection();
PreparedStatement ps = cnt.prepareStatement(sql);
ps.setInt(1, client.getId());
ps.setString(2, client.getNom());
ps.setInt(3, client.getAge());
ps.executeUpdate();
ps.close();
}
catch (SQLException e)
{
throw new RuntimeException(e);
33
Formation : Développement JEE avec Spring
}
finally
{
if (cnt != null) {
try {
cnt.close();
} catch (SQLException e) {}
}
}
}
public Customer chercherClientParId(int id)
{
String sql = "SELECT * FROM client WHERE ID = ?";
Connection cnt = null;
try {
cnt = dataSource.getConnection();
PreparedStatement ps = cnt.prepareStatement(sql);
ps.setInt(1, id);
Customer customer = null;
ResultSet rs = ps.executeQuery();
if (rs.next())
{
client = new Client(
rs.getInt("ID"),
rs.getString("NOM"),
rs.getInt("AGE")
);
}
rs.close();
ps.close();
return client;
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
if (cnt != null) {
try {
34
Formation : Développement JEE avec Spring
cnt.close();
} catch (SQLException e) {}
}
}
}
}
Le fichier datasource.xml
<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-2.5.xsd">
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/formation" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
</beans>
35
Formation : Développement JEE avec Spring
Le fichier : applicationContexte.xml
<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-2.5.xsd">
<import resource="datasource.xml" />
<import resource="client.xml" />
</beans>
package alti.formation.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import alti.formation.spring.dao.ClientDAO
import alti.formation.spring.modele.Client;
public class App
{
public static void main( String[] args )
{
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContexte.xml");
ClientDAO clientDAO = (ClientDAO) context.getBean("clientDAO");
Client client = new Client(1, "test", 40);
clientDAO.inserer(client);
Client existant = clientDAO.chercherParId(1);
System.out.println(existant);
}
}
36
Formation : Développement JEE avec Spring
a) Intégration de JdbcDaoSuppor
En dérivant de la classe JdbcDaoSupport, il n’est plus nécessaire de renseigner la datasource et l’instance JdbcTemplate dans la classe DAOà développer. .
37
Formation : Développement JEE avec Spring
Fichier dataSource.xml
<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-2.5.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/formation" />
<property name="username" value="root" />
<property name="password" value="root" />
</bean>
</beans>
Fichier applicationContext :
<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-2.5.xsd">
<bean id="customerDAO" class="alti.formation.spring.dao.ClientDAO">
<property name="dataSource" ref="dataSource" />
</bean>
</beans>
Fichier applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
38
Formation : Développement JEE avec Spring
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>properties/database.properties</value>
</property>
</bean>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
<!-- Configuration de la session factory d’Hibernate -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="annotatedClasses">
<list>
<value>alti.formation.spring.modele.EcritureComptable </value>
</list>
</property>
39
Formation : Développement JEE avec Spring
</bean>
</beans>
public class App
{
public static void main( String[] args )
{
ApplicationContext appContext =
new ClassPathXmlApplicationContext("applicationContext.xml");
EcritureService ecritureService = (EcritureService) appContext.getBean("ecritureBean");
/** insert **/
Ecriture ecriture = new Ecriture();
ecriture.setCompte("123");
ecritureService.save(ecriture);
/** recherche **/
Piece piece = ecritureService.chercherPieceParNumero("P0112");
System.out.println(piece);
/** mise à jour **/
piece.setPeriode("2012-12");
ecritureService.miseAJour(piece);
/** suppression **/
ecritureService.supprimer(piece);
40
Formation : Développement JEE avec Spring
System.out.println("Fait");
}
}
2.4
41
Formation : Développement JEE avec Spring
Java Transaction (JTA) est une API présente dans la spécification J2EE.
JTA fournit des interfaces Java standards entre un gestionnaire de transaction et les différentes parties impliquées dans un système de transactions distribuées : le gestionnaire
de ressources, le serveur d'application et les applications transactionnelles.
JTA est un protocole de commit à deux phases :
1. 1re phase : Chaque partie prenant part à la transaction distribuée s'engage à verrouiller les données concernées et à valider ces données une fois la transaction terminée
2. 2e phase : Chaque partie valide les changements des données. Cette phase est obligatoire, dès lors que les parties se sont engagées.
Ce protocole de commit à deux phases fonctionne plutôt bien sur les transactions courtes, mais est totalement inefficace en cas de transaction lente où le risque d'une
déconnexion ou bien d'un crash entre les deux phases est élevé, car les verrous restent posés après la première phase et ne sont libérés qu'après la deuxième phase.
Le choix d'un gestionnaire de transactions distribuées est souvent délicat. Généralement pour les applis Web on utilise directement le gestionnaire de transaction du serveur
d'application. Par contre dans le cas d'une application standalone (ou batch) ou d'un mécontentement par rapport au gestionnaire de votre serveur, il faut avoir recours à un
gestionnaire de transactions indépendant. J'ai pu trouver 6 implémentations majeures, que j'ai évalué assez sommairement (mais efficacement je l'espère).
Dans cet article je présenterai les options qui s'offrent à vous, ainsi qu'un exemple de configuration et d'implémentation de transactions distribuées avec Atomikos.
Le cas d'utilisation est une transaction entre un broker JMS, et 2 bases de données. Le broker JMS est ActiveMQ, et les bases données testées sont Oracle et MySQL.
42
Formation : Développement JEE avec Spring
<build>
<pluginmanagement>
<plugins>
<plugin>
<artifactid>maven-eclipse-plugin</artifactid>
<configuration>
<wtpversion>1.5</wtpversion>
<wtpcontextname>blank-app</wtpcontextname>
<springcontext>
43
Formation : Développement JEE avec Spring
<include>${basedir}/src/main/resources/spring/*.xml</include>
</springcontext>
</configuration>
</plugin>
</plugins>
</pluginmanagement>
</build>
<dependencies>
<!-- JTA implementation -->
<dependency>
<groupid>com.atomikos</groupid>
<artifactid>transactions-essentials-all</artifactid>
<version>3.5.9</version>
<exclusions>
<exclusion>
<groupid>org.hibernate</groupid>
<artifactid>hibernate</artifactid>
</exclusion>
</exclusions>
</dependency>
<!-- To run as a standalone application (without server) -->
<dependency>
<groupid>tomcat</groupid>
<artifactid>naming-factory</artifactid>
<version>5.5.23</version>
</dependency>
<dependency>
<groupid>tomcat</groupid>
<artifactid>naming-resources</artifactid>
<version>5.5.23</version>
</dependency>
<!-- Active MQ(broker JMS) -->
<dependency>
44
Formation : Développement JEE avec Spring
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-core</artifactid>
<version>5.2.0</version>
</dependency>
<!-- For tests -->
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.unitils</groupid>
<artifactid>unitils</artifactid>
<version>2.0</version>
<scope>test</scope>
</dependency>
<!-- Spring -->
<dependency>
<groupid>log4j</groupid>
<artifactid>log4j</artifactid>
<version>1.2.12</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring</artifactid>
<version>2.5.5</version>
</dependency>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-aspects</artifactid>
<version>2.5.5</version>
<exclusions>
<exclusion>
45
Formation : Développement JEE avec Spring
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
</exclusion>
<exclusion>
<groupid>org.springframework</groupid>
<artifactid>spring-beans</artifactid>
</exclusion>
</exclusions>
</dependency>
<!-- PERSISTANCE -->
<dependency>
<groupid>hsqldb</groupid>
<artifactid>hsqldb</artifactid>
<version>1.8.0.7</version>
</dependency>
<dependency>
<groupid>org.hibernate</groupid>
<!-- <artifactId>hibernate-core</artifactId>-->
<!-- <version>3.3.2.GA</version>-->
<artifactid>hibernate</artifactid>
<version>3.2.7.ga</version>
</dependency>
<dependency>
<groupid>org.hibernate</groupid>
<artifactid>hibernate-annotations</artifactid>
<version>3.4.0.GA</version>
</dependency>
<dependency>
46
Formation : Développement JEE avec Spring
<groupid>commons-dbcp</groupid>
<artifactid>commons-dbcp</artifactid>
<version>1.2.2</version>
</dependency>
<!-- JDBC Driver -->
<dependency>
<groupid>mysql</groupid>
<artifactid>mysql-connector-java</artifactid>
<version>5.1.8</version>
</dependency>
<dependency>
<groupid>com.oracle</groupid>
<artifactid>ojdbc14</artifactid>
<version>10.2.0.2.0</version>
</dependency>
</dependencies>
</project>
Dans le dossier "resources" pour les utilisateurs de Maven ou dans le classpath, doivent être présent les fichiers jndi.properties et transactions.properties.
java.naming.factory.initial=
org.apache.naming.java.javaU
RLContextFactory Les propriétés nécessaires (présentes dans le fichier) transaction.properties sont les suivantes:
#Factory implementation
47
Formation : Développement JEE avec Spring
#Granularity of output to
the console file
com.atomikos.icatch.console_
log_level=DEBUG
48
Formation : Développement JEE avec Spring
process
com.atomikos.icatch.tm_uniqu
e_name = xa-Spring
Les fichiers Spring à configurer sont multiples car j'ai préféré éclater la configuration. Le fichier Spring chapeau référence un fichier pour la configuration des transactions, deux
autres fichiers pour la configuration respective des bases de données, et un dernier pour le broker JMS.
Commençons par un des fichiers pour la base de données. Je ne présenterai qu'un seul car la configuration est à peu près similaire, mais toutes les informations sur la
configuration des différents type de bases sont présentes (base de données non compatible XA come HsqlDB, base Oracle et Mysql).
NB: Il est important de noter que pour chaque base il faudra associer un sessionFactory différent.
<?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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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/util http://www.springframework.org/schema/util/spring-util-2.0.xsd">
<!-- ==DATABASE SELECTION (use of an XA or a no XA datasource)== -->
<!-- NB: Remember to change the Hibernate dialect -->
<!-- No XA datasource (if you haven't got a XA compatible database) -->
<!-- <bean id="dataSourceDb1"-->
<!-- class="com.atomikos.jdbc.nonxa.AtomikosNonXADataSourceBean"-->
<!-- destroy-method="close">-->
<!-- <property name="uniqueResourceName" value="Database1"/>-->
<!-- <property name="user" value="SA"/>-->
<!-- <property name="password" value="" />-->
<!-- <property name="url" value="jdbc:hsqldb:mem:base1"/>-->
<!-- <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>-->
<!-- <property name="poolSize" value="1"/>-->
49
Formation : Développement JEE avec Spring
50
Formation : Développement JEE avec Spring
<value>com.onx.sample.xa.model.Person</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<!-- <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>-->
<!-- <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>-->
<prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
<prop key="hibernate.jdbc.batch_size">20</prop>
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<prop key="hibernate.connection.release_mode">auto</prop>
<prop key="hibernate.show_sql">true</prop>
<!-- JTA properties -->
<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory</prop>
<prop
key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
</props>
</property>
</bean>
</beans>
Le schéma reste classique pour la partie Java avec des couches "Service", "Model" et "DAO". Le model choisi est une classe "Person" avec 6 attributs (id, name, firstname, byte[]
data, accountBalance, age). Au niveau DAO une classe GenericDAO étend HibernateDAOSupport afin de faciliter l'utilisation du HibernateTemplate. Voici un exemple de
l'implémentation de la DAO pour la sessionFactory2 (rappel: une session factory par base):
@Repository("PersonDAOForDB2")
public class PersonDAOForDB2Impl extends GenericDaoHibernateImpl<Person, Long> implements GenericDao<Person, Long>, PersonDAOForDB2
{
@Autowired
public PersonDAOForDB2Impl(@Qualifier("sessionFactoryDb2")SessionFactory sessionFactory) {
51
Formation : Développement JEE avec Spring
super(Person.class);
setSessionFactory(sessionFactory);
}
}
On reste dans une utilisation classique. Au niveau du service qui sera appelé on a le code suivant:
@Service("personService")
public class PersonServiceImpl implements PersonService {
/** DAO of database 1 **/
@Autowired
private PersonDAOForDB1 personDaoDb1;
/** DAO of database 2 **/
@Autowired
private PersonDAOForDB2 personDaoDb2;
public Person getPersonByIdForDB1(Long id) {
return personDaoDb1.get(id);
}
public Person getPersonByIdForDB2(Long id) {
return personDaoDb2.get(id);
}
/**
* @param personDaoDb1 the personDaoDb1 to set
*/
public void setPersonDaoDb1(PersonDAOForDB1 personDaoDb1) {
this.personDaoDb1 = personDaoDb1;
}
/**
* @param personDaoDb2 the personDaoDb2 to set
*/
public void setPersonDaoDb2(PersonDAOForDB2 personDaoDb2) {
52
Formation : Développement JEE avec Spring
this.personDaoDb2 = personDaoDb2;
}
}
La configuration JMS établie comprend la définition des fabriques de connections pour les transactions XA. Une queue sera utilisée pour cet exemple.
La configuration est commentée. La compréhension me semble facile. Si vous avez des difficultés il faudra se référer à la documentation sur Spring JMS.
53
Formation : Développement JEE avec Spring
Maintenant il nous reste à configurer la gestion des transaction avec Spring et Atomikos, puis écrire les classes de test. Commençons par la gestion des transactions.
<?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: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/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--UserTransactionManager to manage transactions and set properties-->
<bean id="userTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<!--Indicate when close is called, if we should force transactions to terminate-->
<property name="forceShutdown" value="false" />
</bean>
<!-- UserTransaction (allow to manage transactions explicitly) -->
<bean id="userTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<!--Configure the Spring framework to use JTA transactions from Atomikos-->
<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
54
Formation : Développement JEE avec Spring
Les tests
Je me suis appuyé sur Unitils afin de développer les classes de tests. Et je me suis retrouvé confronté à des erreurs sur les transactions. En passant par des Main java, je n'avais
aucun souci. La petite astuce est de penser à désactiver la gestion des transactions d'Unitils qui est activé par défaut.
Maintenant que vous êtes avertis, passons à l'implémentation des scénarios.
Classe de Services
La classes de service qui sera finalement appelée est la suivante (la classe personService était utilie pour des petits tests sur les DAO).
**
* <code>XaTransactions</code> does distributed transactions with
* different databases and JMS broker.
*
* @author Fabrice LUCIEN <a href="mailto:flucien@on-x.com"/>
*/
@Service("xaTransactions")
public class XaTransactionsImpl implements XaTransactions {
/** Logger **/
private Logger logger=Logger.getLogger(this.getClass());
/** DAO of database 1 **/
@Autowired
private PersonDAOForDB1 personDaoDb1;
/** DAO of database 2 **/
@Autowired
private PersonDAOForDB2 personDaoDb2;
55
Formation : Développement JEE avec Spring
56
Formation : Développement JEE avec Spring
logger.info("We didn't get any message. Timeout has probably been reached...");
return;
}
* In the test 'twoDatabasesRollback' the rollback should work for these two databases.
*
* @author Fabrice LUCIEN <a href="mailto:flucien@on-x.com"/>
*/
@RunWith(UnitilsJUnit4TestClassRunner.class)
//Avoid that Unitils manage the begin and the end of the transaction
@Transactional(TransactionMode.DISABLED)
@SpringApplicationContext( { "classpath:/spring/spring.xml" })
public class TwoDatabases {
/** Logger **/
private Logger logger=Logger.getLogger(this.getClass());
/** XA services **/
@SpringBeanByType
private XaTransactions xaTransactions;
/** Person services **/
@SpringBeanByType
private PersonService personService;
@Test
public void twoDatabasesCommit() {
xaTransactions.twoDatabase(false);
Long personIdInsertedInDB1 = xaTransactions.getLastPersonIdForDB1();
Long personIdInsertedInDB2 = xaTransactions.getLastPersonIdForDB2();
Assert.assertNotNull(personIdInsertedInDB1);
Assert.assertNotNull(personIdInsertedInDB2);
Assert.assertNotNull(personService.getPersonByIdForDB1(personIdInsertedInDB1));
Assert.assertNotNull(personService.getPersonByIdForDB2(personIdInsertedInDB2));
}
@Test
58
Formation : Développement JEE avec Spring
/** XA services **/
@SpringBeanByType
private XaTransactions xaTransactions;
/** Person services **/
@SpringBeanByType
private PersonService personService;
/** Producers **/
@SpringBeanByType
private Producer producer;
/** bean to send **/
private Person person;
@Before
public void initData() {
person = new Person();
person.setFirstname("Mickeal");
person.setName("Jackson");
}
@Test
public void jmsAndDBCommit() {
//Sends the message
producer.sendPersonInfo(person);
logger.info("A message was sent on the queue");
//Gets the message and updates the DB in one transaction
xaTransactions.consumeJmsMsgAndUpdateDB(false);
//Check if the DB has been updated
Long personIdInsertedInDB1 = xaTransactions.getLastPersonIdForDB1();
Person personForDB1=personService.getPersonByIdForDB1(personIdInsertedInDB1);
Assert.assertEquals(person.getFirstname(),personForDB1.getFirstname());
//TODO check the message was consumed instead looking the monitor pages
}
60
Formation : Développement JEE avec Spring
@Test
public void jmsAndDBRollback() {
//Sends the message
producer.sendPersonInfo(person);
logger.info("A message was sent on the queue");
//Gets the message and updates the DB in one transaction
try {
xaTransactions.consumeJmsMsgAndUpdateDB(true);//An exception is throw
} catch (NullPointerException e) {
logger.info("Transaction failed as expected");
}
//Check if the DB has been updated
Long personIdInsertedInDB1 = xaTransactions.getLastPersonIdForDB1();
Person personForDB1=personService.getPersonByIdForDB1(personIdInsertedInDB1);
Assert.assertNull(personForDB1);
//TODO check the message was not consumed instead looking the monitor pages
}
}
61
Formation : Développement JEE avec Spring
Spring permet une gestion et une propagation des transactions. Depuis Spring 2.0, les transactions sont mises en oeuvre en utilisant l'AOP.
Généralement, c'est la couche service qui assure la gestion des transactions des traitements. La déclaration des méthodes qui doivent être transactionnelles peut se faire par
déclaration dans la configuration de Spring ou par des annotations.
L'utilisation des transactions peut se faire par déclaration ou par programmation en utilisant une API dédiée. L'utilisation des transactions de manière déclarative est la façon la
plus simple de les mettre en oeuvre car c'est celle qui limite les impacts dans le code de l'application.
La déclaration du comportement transactionnel se fait au niveau des méthodes de toutes les classes concernées. Cependant, Spring n'est pas en mesure de propager un contexte
transactionnel dans des appels de méthodes distantes.
Depuis Spring 2.0, il n'est plus nécessaire de déclarer un bean de type TransactionProxyFactoryBean mais il faut utiliser les tags de l'espace de nommage tx.
La mise en oeuvre des transactions avec Spring se fait essentiellement de manière déclarative : la façon la plus simple d'utiliser une transaction avec Spring est d'ajouter la
déclaration de l'espace de nommage tx et le tag <tx:annotation-driven/> dans le fichier de configuration et d'utiliser le tag @Transaction sur les classes et/ou les méthodes
concernées.
62
Formation : Développement JEE avec Spring
Une transaction peut être logique ou physique. Une transaction logique est gérée par Spring : une ou plusieurs transactions logiques permettent à Spring de déterminer le statut
de la transaction physique.
La propagation PROPAGATION_REQUIRED créée une transaction logique pour chaque méthode dont le contexte transactionnel possède ce type de propagation. Durant la portée de
cette transaction logique, celle-ci peut être validée ou annulée.
Comme les transactions logiques peuvent être imbriquées, pour indiquer à une transaction englobante qu'une transaction sous-jacente a été annulée, une exception de type
UnexpectedRollbackException est levée.
La propagation PROPAGATION_REQUIRES_NEW créé une nouvelle transaction indépendante pour chaque méthode dont le contexte transactionnel possède ce type de propagation.
Chaque contexte transactionnel dispose de sa propre transaction physique. Le rollback de la transaction n'a aucune incidence sur le rollback d'une transaction englobante.
La propagation PROPAGATION_NESTED utilise une seule transaction physique avec des savepoints. Il est donc possible de faire un rollback dans le contexte transactionnel jusqu'au
précédent savepoint sans annuler l'intégralité de la transaction physique sous-jacente qui poursuit son exécution. Le gestionnaire de transaction doit permettre un support des
savepoints ce qui pour le moment n'est possible qu'avec le DataSourceTransactionManager qui utilise les transactions JDBC.
Exemple :
63
Formation : Développement JEE avec Spring
64
Formation : Développement JEE avec Spring
<aop:config>
<aop:pointcut id="serviceMethodes" expression="execution(*alti.formation.spring.service.*ServiceImpl.*(..))" />
<aop:advisor advice-ref="serviceTxAdvice" pointcut-ref="serviceMethodes" />
</aop:config>
<aop:config>
<aop:pointcut id="daoMethodes"
expression="execution(*alti.formation.spring.dao.*DaoImpl*.*(..))" />
<aop:advisor advice-ref="daoTxAdvice" pointcut-ref="daoMethodes" />
</aop:config>
</beans>
Un advice est défini sous le nom txAdvice en lui associant le TransactionManager grâce à l'attribut transaction-manager : les méthodes dont le nom commence par get sont en
lecture seule, les autres méthodes sont en lecture/écriture qui est le mode par défaut.
Le tag <tx:advice/> possède plusieurs attributs :
nom de
Rôle Valeur par défaut
l'attribut
par défaut, c'est le timeout du gestionnaire de transactions sous-jacent utilisé, ou aucun si aucun
Timeout Préciser le timeout avant le rollback de la transaction
timeout n'est supporté
Name nom de la ou des méthodes concernées en utilisant un motif dans lequel le caractère * peut être utilisé (exemple : get*) (obligatoire)
65
Formation : Développement JEE avec Spring
rollback-for la ou les exceptions (séparées par un caractère ";") qui provoquent un rollback de la transaction
no-rollback-for la ou les exceptions (séparées par un caractère ";") qui ne provoquent pas un rollback de la transaction
Remarque : par défaut, toutes les exceptions de type RuntimeException provoquent un rollback mais les exceptions de type checked ne provoquent pas de rollback.
Le tag <aop:config> permet la configuration du tissage des aspects relatifs aux transactions en définissant un point de coupe précisé sous la forme d'une expression régulière
d'AspectJ fournie comme valeur de l'attribut expression.
Grace au tissage, l'advice sera exécuté lors de l'invocation de chaque méthode définie par le point de coupe.
Généralement, toutes les méthodes des services doivent être transactionnelles. Pour cela, le point de coupe doit utiliser une expression qui désigne toutes les méthodes et tous
les services.
Exemple :
<aop:config>
<aop:pointcut id="serviceMethodes" expression="execution(*
alti.formation.spring.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethodes"/>
</aop:config>
La gestion des transactions dans les services n'est pas toujours aussi générique et il peut être nécessaire de définir plusieurs pointcuts et advisors pour différentes configurations
transactionnelles.
Exemple :
<?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.5.xsd
66
Formation : Développement JEE avec Spring
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<aop:config>
<aop:pointcut id="defaultServiceOperation"
expression="execution(* alti.formation.spring.*Service.*(..))" />
<aop:pointcut id="noTxServiceOperation"
expression="execution(*alti.formation.spring.*Cache.*(..))" />
<aop:advisor pointcut-ref="defaultServiceOperation"
advice-ref="defaultTxAdvice" />
<aop:advisor pointcut-ref="noTxServiceOperation"
advice-ref="noTxAdvice" />
</aop:config>
<tx:advice id="defaultTxAdvice">
<tx:attributes>
tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<tx:advice id="noTxAdvice">
<tx:attributes>
<tx:method name="*" propagation="NEVER" />
</tx:attributes>
</tx:advice>
<bean id="personneService" class="alti.formation.spring.service.PersonneServiceImpl" />
<bean id="personneCache" class="alti.formation.spring.service.cache.PersonneCache" />
</beans>
67
Formation : Développement JEE avec Spring
Exemple :
package alti.formation.spring.service;
import java.util.List;
import alti.formation.spring.Personne;
public interface PersonneService
{
void ajouter(Personne personne);
Personne getParId(long id);
List<Personne> getTous();
void modifier(Personne personne);
}
Exemple :
package alti.formation.spring.service;
import java.util.List;
import alti.formation.spring.Personne;
public class PersonneServiceImpl implements PersonneService {
@Override
public void ajouter(final Personne personne) {
throw new UnsupportedOperationException();
}
68
Formation : Développement JEE avec Spring
@Override
public Personne getParId(final long id) {
throw new UnsupportedOperationException();
}
@Override
public List<Personne> getTous() {
throw new UnsupportedOperationException();
}
@Override
public void modifier(final Personne personne) {
throw new UnsupportedOperationException();
}
}
Comme l'implémentation de toutes les méthodes du service lève une exception de type RuntimeException, les transactions provoqueront un rollback.
Les méthodes suffixées par get sont en lecture seule (read-only) alors que les autres méthodes sont utilisées pour des mises à jour (read-write).
Exemple :
<?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.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
69
Formation : Développement JEE avec Spring
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:methodname="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="personneServiceOperation"
expression="execution(*
alti.formation.spring.service.PersonneService.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="personneServiceOperation" />
</aop:config>
Dans l'exemple ci-dessous, le point de coupe concerne toutes les méthodes de la classe PersonneService en définissant un advisor qui lie le point de coupe et l'advice.
Le PlatformTransactionManager est défini sous la forme d'un bean nommé txManager.
L'utilisation des transactions est alors transparente dans le code appelant.
Exemple :
package alti.formation.spring;
import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;
70
Formation : Développement JEE avec Spring
import alti.formation.spring.Personne;
import alti.formation.spring.PersonneService;
public class MonApp
{
private static Logger LOGGER = Logger.getLogger(MonApp.class);
public static void main(final String[] args) throws Exception
{
LOGGER.info("Debut de l'application");
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] {"appContext.xml" });
L'application de test charge le contexte, lui demande une instance du service et invoque sa méthode ajouter().
Le niveau de traces dans fichier de configuration de Log4J est configuré sur debug pour permettre de voir le détail des actions réalisées par Spring pour gérer les transactions .
Exemple :
<?xml version="1.0" encoding= "UTF-8" ?>
71
Formation : Développement JEE avec Spring
Résultat :
2013-04-28 22:16:07,937 INFO [alti.formation.spring.MonApp] Debut de l'application
...
2013-04-28 22:16:10,000 DEBUG [org.springframework.beans.factory.support.
DefaultListableBeanFactory] Returning cached instance of singleton bean 'personneService'
2013-04-28 22:16:10,000 INFO [alti.formation.spring.MonApp] Debut invocation du service
2013-04-28 22:16:10,031 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Creating new transaction with name
72
Formation : Développement JEE avec Spring
[alti.formation.service.PersonneServiceImpl.ajouter]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2013-04-28 22:16:11,093 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Acquired Connection [jdbc:derby://localhost:1527/MaBaseDeTest,
UserName=APP, Apache Derby Network Client JDBC Driver] for JDBC transaction
2013-04-28 22:16:11,109 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Switching JDBC Connection [jdbc:derby://localhost:1527/
MaBaseDeTest, UserName=APP, Apache Derby Network Client JDBC Driver] to manual commit
2013-04-28 22:16:11,109 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Initiating transaction rollback
2013-04-28 22:16:11,109 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Rolling back JDBC transaction on Connection [jdbc:derby:
//localhost:1527/MaBaseDeTest, UserName=APP, Apache Derby Network Client JDBC Driver]
2013-04-28 22:16:11,109 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Releasing JDBC Connection [jdbc:derby://localhost:1527/
MaBaseDeTest, UserName=APP, Apache Derby Network Client JDBC Driver] after transaction
2013-04-28 22:16:11,109 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils]
Returning JDBC Connection to DataSource
2013-04-28 22:16:11,109 ERROR [alti.formation.spring.MonApp]
exception java.lang.UnsupportedOperationException interceptee
2013-04-28 22:16:11,109 INFO [alti.formation.spring.MonApp] Fin invocation du service
2013-04-28 22:16:11,109 INFO [alti.formation.spring.MonApp] Fin de l'application
Si la méthode du service ne lève pas d'exception durant son invocation, la transaction est validée par un commit.
Résultat :
2011-04-28 22:18:05,609 INFO
[alti.formation.spring.MonApp] Debut de l'application
...
2011-04-28 22:18:07,625 INFO
[alti.formation.spring.MonApp] Debut invocation du service
2011-04-28 22:18:07,671 DEBUG
[org.springframework.jdbc.datasource.DataSourceTransactionManager] Creating new
transaction with name [alti.formation.spring.service.PersonneServiceImpl.ajouter]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2011-04-28 22:18:08,703 DEBUG
[org.springframework.jdbc.datasource.DataSourceTransactionManager] Acquired
73
Formation : Développement JEE avec Spring
Exemple :
74
Formation : Développement JEE avec Spring
<beans>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="datasource" ref="dataSource"
</bean>
<tx:annotation-driven transaction-manager="txManager"/>
<!-- ... -->
</beans>
La définition des transactions avec une annotation est plus simple à mettre en oeuvre, car il suffit d'annoter chaque méthode ou classe concernée avec @Transactional au lieu de
la définir par des expressions régulières dans le fichier de configuration.
L'annotation org.springframework.transaction.annotation.Transactionnal s'utilise sur une classe ou une méthode. Sur une classe, elle s'applique automatiquement sur toutes les
méthodes publiques de la classe.
L'annotation @Transactional possède plusieurs attributs :
propagation : précise le mode de propagation de la transaction grâce à une énumération de type Propagation. La valeur par défaut est Propagation.REQUIRED
readonly : booléen qui précise de façon informative au système de gestion des transactions sous-jacent si la transaction est en lecture seule (true) ou si elle effectue des
mises à jour (false)
isolation : précise le niveau d'isolation de la transaction grâce à une énumération de type Isolation. La valeur par défaut est Isolation.DEFAULT
timeout : entier qui précise le timeout de la transaction
Exemple :
package alti.formation.spring.service;
@Service("personneService")
@Transactional
public class PersonneServiceImpl implements PersonneService {
//...
@Transactional(readOnly=true)
public List<Personne> findAll() throws ServiceException {
//...
}
75
Formation : Développement JEE avec Spring
//...
}
Il est fortement recommandé d'utiliser l'annotation @Transactional sur des classes et non sur des interfaces.
L'avantage de mettre en oeuvre les transactions par AOP est qu'il n'est pas nécessaire d'utiliser une API de Spring dans le code pour mettre en oeuvre les transactions. La mise en
oeuvre reste aussi la même quelque soit l'implémentation du gestionnaire de transactions utilisées : la seule chose qui change c'est la configuration du transaction manager
utilisé.
lecture/écriture(true) true
La simple utilisation de l'annotation @Transactional ne suffit pas car c'est simplement des métadonnées : il faut obligatoirement utiliser le tag <tx:annotation-driven> dans la
configuration pour permettre à Spring d'ajouter les traitements relatifs aux aspects transactionaux sur les méthodes annotées.
Le tag <tx:annotation-driven> possède plusieurs attributs :
transaction- nom du bean qui encapsule le gestionnaire de transaction (obligatoire uniquement si le nom ne correspond à la valeur par
transaction-manager
manager défaut)
mode les valeurs possibles sont proxy (utilisation de proxys) et aspectj (tissage des aspects avec AspectJ) proxy
Permet de préciser le type de proxy utilisé (true : proxy reposant sur les interfaces, false : proxy reposant sur les classes). Ne
proxy-target-class False
doit être utilisé que si le mode est proxy
Ordered.LOWEST
order Permet de définir l'ordre des traitements exécutés sur les beans annotés avec @Transactional
PRECEDENCE
Attention : le tag <tx:annotation-driven> ne recherche les beans annotés avec @Transactional que dans le contexte dans lequel ils sont définis.
L'annotation @Transactional peut être utilisée sur une classe ou sur une méthode. Utilisée sur une classe, elle s'applique par défaut sur toutes les méthodes public de la classe
sauf si la méthode est elle même annotée avec @Transactional. Dans ce cas, c'est l'annotation sur la méthode qui est utilisée.
Exemple :
@Transactional(readOnly = true)
public class PersonneServicelmpl implements PersonneService {
public Personne getParld(long id) {
// traitements de la methode
}
@Transactional(readOnly = false, propagation = Propagation.REQUIRESNEW)
public void modifier(Personne personne) {
// traitements de la methode
}
}
77
Formation : Développement JEE avec Spring
Seules les méthodes public doivent être annotées avec @Transactional lors de l'utilisation de proxys. Si des méthodes package-friendly, protected ou private sont annotées avec
@Transactional, aucune erreur n'est produite à la compilation mais ces méthodes seront ignorées lors de l'utilisation des proxys.
Il est fortement recommandé de n'utiliser l'annotation @Transactional que dans des classes concrètes surtout dans le mode par défaut, le mode proxy.
Attention : dans le mode proxy, seules les invocations de méthodes depuis d'autres classes seront transactionnelles. Les invocations d'une méthode de la classe par une autre
méthode de la classe ne sont pas transactionnelles même si la méthode invoquée est annotée avec @Transactional car ces invocations ne sont pas interceptées par le proxy.
Dans ce cas, il faut utiliser le mode AspectJ pour permettre un tissage des classes avec les aspects relatifs à la gestion des transactions pour les méthodes annotées. L'utilisation
de ce mode requiert que la bibliothèque spring-aspects.jar soit ajoutée au classpath et le tissage (load-time weaving ou compile-time weaving) soit activé.
L'exemple de cette section va utiliser Spring 3, AspectJ (en mode Load Time Weaving), JavaDB (en mode client/serveur), log4J.
La déclaration des transactions en utilisant les annotations est plus simple à mettre en oeuvre que la déclaration dans la configuration.
Il suffit d'utiliser l'annotation @Transactional sur les méthodes ou sur les classes concernées .
Exemple :
package alti.formation.spring.service;
import java.util.List;
import alti.formation.spring.Personne;
78
Formation : Développement JEE avec Spring
public interface PersonneService {
void ajouter(Personne personne);
Personne getParId(long id);
List<Personne> getTous();
void modifier(Personne personne);
}
Exemple :
package alti.formation.spring.service;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
import alti.formation.spring.Personne;
public class PersonneServiceImpl implements PersonneService {
@Override
@Transactional
public void ajouter(final Personne personne) {
throw new UnsupportedOperationException();
}
@Override
@Transactional(readOnly = true)
public Personne getParId(final long id) {
throw new UnsupportedOperationException();
}
@Override
@Transactional(readOnly = true)
public List<Personne> getTous() {
79
Formation : Développement JEE avec Spring
Dans cette implémentation, toutes les méthodes transactionnelles lève une exception, ce qui permet de tester le rollback de la transaction.
La configuration du contexte est simplifiée car il suffit de déclarer le gestionnaire de transactions à utiliser et d'utiliser le tag <tx:annotation-driven>.
Exemple :
<?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.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<tx:annotation-driven mode="aspectj" transaction-manager="txManager" />
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost/MaBaseDeTest" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource" />
80
Formation : Développement JEE avec Spring
/bean>
<bean id="personneService" class="alti.formation.spring.service.PersonneServiceImpl" />
</beans>
L'attribut transaction-manager du tag <tx:annotation-driven> permet de préciser l'instance du gestionnaire de transactions à utiliser. Cet attribut peut être facultatif si le nom du
bean du gestionnaire de transaction est "transactionManager".
Exemple :
package alti.formation.spring;
import org.apache.log4j.Logger;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import alti.formation.spring.entite.Personne;
import alti.formation.spring.service.PersonneService;
public class MonApp {
private static Logger LOGGER = Logger.getLogger(MonApp.class);
public static void main(final String[] args) throws Exception {
LOGGER.info("Debut de l'application");
ClassPathXmlApplicationContext appContext = new ClassPathXmlApplicationContext(new String[] { "appContext.xml" });
PersonneService personneService = (PersonneService) appContext.getBean("personneService");
LOGGER.info("Debutinvocation du service");
try {
personneService.ajouter(newPersonne());
} catch (Exception e) {
LOGGER.error("exception" + e.getClass().getName() + " interceptee");
}
81
Formation : Développement JEE avec Spring
L'application de test charge le contexte, lui demande un instance du service et invoque sa méthode ajouter().
Le niveau de traces dans fichier de configuration de Log4J est configuré sur debug pour permettre de voir le détail des actions réalisées par Spring pour gérer les transactions.
Exemple :
<?xml version="1.0" encoding= "UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration
xmlns:log4j="http://jakarta.apache.org/log4j/">
<appender name="stdout" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %p [%c] - %m%n"/>
</layout>
</appender>
<root>
<priority value="debug"/>
<appender-ref ref="stdout"/>
</root>
</log4j:configuration>
Résultat :
2011-04-26 22:17:48,453 INFO [alti.formation.spring.MonApp] Debut de l'application
2011-04-26 22:17:50,187 INFO [alti.formation.spring.MonApp] Debut invocation du service
...
82
Formation : Développement JEE avec Spring
Si la méthode du service ne lève pas d'exception durant son invocation, la transaction est validée par un commit.
Résultat :
2011-04-26 22:25:17,484 INFO
[alti.formation.spring.MonApp] Debut de l'application
...
2011-04-26 22:25:19,250 INFO
[alti.formation.spring.MonApp] Debut invocation du service
83
Formation : Développement JEE avec Spring
84
Formation : Développement JEE avec Spring
Si les transactions sont définies dans la configuration du contexte, les attributs rollback-for et no-rollback-for du tag <tx:method> permettent respectivement de préciser la ou les
types d'exceptions qui vont provoquer un rollback ou non.
Exemple :
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="MonException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
Dans cet exemple, un rollback de la transaction sera exécuté par Spring si une exception de type MonException est levée durant les traitements du contexte transactionnel.
Exemple :
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" no-rollback-for="MonAutreException"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
85
Formation : Développement JEE avec Spring
Dans cet exemple, un commit de la transaction sera exécuté par Spring si une exception de type MonAutreException est levée durant les traitements du contexte transactionnel
sans autre exception.
Exemple :
<tx:advice id="txAdvice">
<tx:attributes>
<tx:method name="*" rollback-for="Throwable" no-rollback-for="MonException"/>
</tx:attributes>
</tx:advice>
Dans l'exemple ci-dessus, seule l'exception MonException ne va pas provoquer un rollback de la transaction.
Il est aussi possible de forcer le rollback de la transaction par programmation en utilisant l'API.
Exemple :
public void maMethode() {
try {
// traitements
} catch (MonException ex) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
Cette solution impose d'utiliser l'API de Spring ce qui n'est pas la meilleure solution : il est préférable dans la mesure du possible d'utiliser l'approche déclarative.
L'attribut noRollbackFor permet de préciser un tableau d'exceptions héritant de Throwable qui ne provoquent pas un rollback de la transaction si elles sont levées durant les
traitements.
L'attribut noRollbackForClassname permet de préciser un tableau de noms de classes héritant de Throwable qui ne provoquent pas un rollback de la transaction s'ils sont levés
durant les traitements.
Ces quatre attributs permettent de configurer de façon précise les conditions selon lesquelles un rollback de la transaction sera fait par les traitements de l'annotation
@Transactional.
Les transactions sont mises en oeuvre grâce à l'AOP mais il est aussi possible d'utiliser l'AOP pour ses propres besoins sur des méthodes transactionnelles.
Spring offre un moyen de configurer l'ordre d'exécution de ces aspects en implémentant l'interface Ordered.
Exemple :
package alti.formation.spring;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.core.Ordered;
public class MonitoringPerf implements Ordered {
private int order;
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Object executer(ProceedingJoinPoint call) throws Throwable {
Object returnValue;
StopWatch clock = new StopWatch(getClass().getName());
try {
87
Formation : Développement JEE avec Spring
clock.start(call.toShortString());
returnValue = call.proceed();
}
finally {
clock.stop();
System.out.println(clock.prettyPrint());
}
return returnValue;
}
}
Il faut définir l'aspect dans le fichier de configuration du contexte et préciser l'ordre d'invocation des aspects.
Le fichier de configuration en déclarant les transactions par annotations
Exemple :
<?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.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<tx:annotation-driven mode="aspectj"
transaction-manager="txManager" order="99" />
<aop:config>
<aop:aspect id="monitorerPerfAspect" ref="monitorerPerf">
<aop:pointcut id="methodeService"
expression="execution(* alti.formation.spring.service.*ServiceImpl.*(..))" />
<aop:around method="executer" pointcut-ref="methodeService" />
</aop:aspect>
88
Formation : Développement JEE avec Spring
</aop:config>
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost/MaBaseDeTest" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource" />
</bean>
<bean id="monitorerPerf" class="alti.formation.spring.MonitorerPerf">
<property name="order" value="1" />
</bean>
<bean id="personneService" class="alti.formation.spring.service.PersonneServiceImpl" />
</beans>
L'ordre d'invocation des aspects est défini grâce à la valeur fournie aux attributs order du bean qui encapsule l'aspect et de l'attribut order du tag <annotation-driven>.
Le fichier de configuration en déclarant les transactions dans le fichier de configuration
Exemple :
<?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.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
89
Formation : Développement JEE avec Spring
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="personneServiceOperation" expression="execution(* alti.formation.spring.PersonneService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="personneServiceOperation"
order="99" />
<aop:aspect id="monitorerPerfAspect" ref="monitorerPerf">
<aop:around method="executer" pointcut-ref="personneServiceOperation" />
</aop:aspect>
</aop:config>
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
<property name="url" value="jdbc:derby://localhost/MaBaseDeTest" />
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="datasource" />
</bean>
<bean id="monitorerPerf" class="alti.formation.MonitorerPerf">
<property name="order" value="1" />
</bean>
<bean id="personneService" class="alti.formation.PersonneServiceImpl" />
</beans>
90
Formation : Développement JEE avec Spring
L'ordre d'invocation des aspects est défini grâce à la valeur fournie aux attributs order du bean qui encapsule l'aspect et de l'advisor qui concerne la gestion des transactions.
L'exécution de l'application affiche le temps d'exécution suite à l'invocation de la méthode
Résultat :
2011-04-28 22:27:47,375 INFO [alti.formation.spring.MonApp] Debut de l'application
...
2011-04-28 22:27:49,578 INFO [alti.formation.spring.MonApp] Debut invocation du service
2011-04-28 22:27:49,593 DEBUG [org.springframework.beans.factory.support.
DefaultListableBeanFactory] Returning cached instance of singleton bean 'monitorerPerf'
2011-04-28 22:27:49,640 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Creating new transaction with name
[ alti.formation.spring.PersonneServiceImpl.ajouter]:
PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2011-04-28 22:27:50,687 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Acquired Connection
[jdbc:derby://localhost:1527/MaBaseDeTest, UserName=APP, Apache Derby Network Client
JDBC Driver] for JDBC transaction
2011-04-28 22:27:50,703 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Switching JDBC Connection
[jdbc:derby://localhost:1527/MaBaseDeTest, UserName=APP, Apache Derby Network Client
JDBC Driver] to manual commit
2011-04-28 22:27:50,703 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Initiating transaction commit
2011-04-28 22:27:50,703 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Committing JDBC transaction on Connection [jdbc:derby:
//localhost:1527/MaBaseDeTest, UserName=APP, Apache Derby Network Client JDBC Driver]
2011-04-28 22:27:50,703 DEBUG [org.springframework.jdbc.datasource.
DataSourceTransactionManager] Releasing JDBC Connection
[jdbc:derby://localhost:1527/MaBaseDeTest, UserName=APP, Apache Derby Network Client
JDBC Driver] after transaction
2011-04-28 22:27:50,703 DEBUG [org.springframework.jdbc.datasource.DataSourceUtils]
Returning JDBC Connection to DataSource
2011-04-28 22:27:50,703 DEBUG [alti.formation.spring.MonitorerPerf]
temps d'execution : StopWatch
'com.jmdoudoux.test.spring.aspect.MonitorerPerf': running time (millis) = 1094
-----------------------------------------
ms % Task name
91
Formation : Développement JEE avec Spring
-----------------------------------------
01094 100 % execution(PersonneService.ajouter(..))
2011-04-28 18:27:50,703 INFO [alti.formation.spring.MonApp] Fin invocation du service
2011-04-28 18:27:50,703 INFO [alti.formation.spring.MonApp] Fin de l'application
Le principe de la classe TransactionTemplate repose sur l'utilisation de callbacks comme pour les autres templates Spring.
Il faut écrire une implémentation de TransactionCallback, généralement sous la forme d'une classe anonyme interne, qui va contenir les traitements à exécuter dans le contexte
transactionnel.
Il suffit alors de passer une instance de cette implémentation en paramètre de la méthode execute() de la classe TransactionTemplate.
Exemple :
package alti.formation.spring;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import alti.formation.spring.Personne;
92
Formation : Développement JEE avec Spring
Exemple :
package alti.formation.spring;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
93
Formation : Développement JEE avec Spring
import alti.formation.spring.Personne;
public class MonServiceImpl implements MonService {
private final TransactionTemplate transactionTemplate;
public MonServiceImpl(final PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
protected void maMethode(final Personne personne) {
// traitement de la methode
}
public Object maMethodeTransactionelle(final Personne personne) {
return transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(final TransactionStatus status) {
maMethode(personne);
}
});
}
}
Il est possible de demander explicitement l'annulation de la transaction dans le code du callback en invoquant la méthode setRollbackOnly() de l'instance de TransactionStatus
fournie en paramètre.
Exemple :
package alti.formation.spring;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
94
Formation : Développement JEE avec Spring
import alti.formation.spring.Personne;
public class MonServiceImpl implements MonService {
private final TransactionTemplate transactionTemplate;
public MonServiceImpl(final PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
protected boolean maMethode(final Personne personne) {
// traitement de la methode
return true;
}
public Object maMethodeTransactionelle(final Personne personne) {
return transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(final TransactionStatus status) {
boolean resultat = false;
try {
resultat = maMethode(personne);
} catch (MonException ex) {
status.setRollbackOnly();
}
return resultat;
}
});
}
}
La classe TransactionTemplate possède plusieurs méthodes pour permettre de configurer le contexte transactionnel.
Exemple :
01.package com.jmdoudoux.test.spring.service;
95
Formation : Développement JEE avec Spring
02.
03.import org.springframework.transaction.PlatformTransactionManager;
04.import org.springframework.transaction.TransactionDefinition;
05.import org.springframework.transaction.TransactionStatus;
06.import org.springframework.transaction.support.TransactionCallback;
07.import org.springframework.transaction.support.TransactionTemplate;
08.
09.import com.jmdoudoux.test.spring.entite.Personne;
10.
11.public class MonServiceImpl implements MonService {
12.
13.private final TransactionTemplate transactionTemplate;
14.
15.public MonServiceImpl(final PlatformTransactionManager transactionManager) {
16.this.transactionTemplate = new TransactionTemplate(transactionManager);
17.this.transactionTemplate
18..setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
19.this.transactionTemplate.setTimeout(60);
20.
21.}
22.
23.// ...
24.}
Les instances de TransactionTemplate sont threadsafe mais elles contiennent la configuration. Il est donc possible de partager une instance de TransactionTemplate mais il est
nécessaire d'avoir autant d'instance que de configuration différente.
Il est donc possible de déclarer un bean de type TransactionTemplate et de le paramétrer dans la configuration puis de l'injecter dans les services qui en ont besoin .
Exemple :
<bean id="monTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED" />
<property name="timeout" value="30" />
</bean>
96
Formation : Développement JEE avec Spring
Méthode Rôle
boolean hasSavepoint() Renvoie un booléen qui précise si la transaction a été créée comme englobée dans une transaction possédant un savepoint
boolean isCompleted() Renvoie un booléen qui précise si la transaction est terminée (par un rollback ou un commit)
boolean isRollbackOnly() Renvoie un booléen qui précise si la transaction est marquée comme devant être annulée
void setRollbackOnly() Demande l'annulation de la transaction
Exemple :
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setName("MaTransaction");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// les traitements
} catch (MonException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
97
Formation : Développement JEE avec Spring
98
Formation : Développement JEE avec Spring
4 Scheduler
Le framework Spring permet de créer des traitements de types taches ordonnancées (scheduled tasks).
- On reference le bean et la méthode qui seront executées comme une tache ordonnancée (plannifiée):
<bean id="schedulerTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean">
<property name="targetObject" ref="runMeTask" />
<property name="targetMethod" value="printMe" />
</bean>
Remarques :
a)
- Enfin, on déclare dans le bean Spring qui correspond à la fabrique de de Timer la tache programmée :
99
Formation : Développement JEE avec Spring
<bean class="org.springframework.scheduling.timer.TimerFactoryBean">
<property name="scheduledTimerTasks">
<list>
<ref local="timerTask" />
</list>
</property>
</bean>
Le cntenu global du fichier de définition du contexte d’application Spring (applicationContext.xml) est le suivant :
<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-2.5.xsd">
100
Formation : Développement JEE avec Spring
No code need to call the scheduler task, the TimerFactoryBean will run your schedule task during start up. As result, Spring scheduler will run the
printMe() method every 60 seconds, with a 1 second delay for the first time of execution.
Conclusion
Most of the Spring developers are just implements the ‘Around advice ‘, since it can apply all the advice type, but a better practice should choose the
most suitable advice type to satisfy the requirements.
101
Formation : Développement JEE avec Spring
Il est possible d’activer le protocole Hessian 2 (recommandé) qui est plus performant en changeant la propriété proxyFactory du bean déclaré
102
Formation : Développement JEE avec Spring
<property name="proxyFactory">
<bean class="com.caucho.hessian.client.HessianProxyFactory">
<property name="hessian2Request" value="true" />
<property name="hessian2Reply" value="true" />
</bean>
</property>
Pour Burlap et http invoker les Proxy à utiliser sont les suivants :
5 Intégration de Spring
6 Mise en place d’un environnement de développement Java/Eclipse/Maven
6.1 Création d’un projet Maven/Eclipse pour Spring 2.5
a) Générer la structure du projet Maven
mvn archetype:generate -DgroupId=alti.formation.spring -DartifactId=Exemples
103
Formation : Développement JEE avec Spring
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
d) Créer un bean
package alti.formation.spring;
/**
* Spring bean
*
*/
public class HelloWorld
{
private String name;
public void setName(String name) {
this.name = name;
}
public void printHello() {
104
Formation : Développement JEE avec Spring
g)
105