Vous êtes sur la page 1sur 309

Framework J2EE Spring -

MICDA-M2-G1 / G2

Jamal EL MOUTAOUKIL
Chef de projet / architecte technique
jelmoutaoukil@gmail.com
1
Présentation de la formation
 5 jours pour maitriser le Framework Spring
 Mettre en place Spring sur les différentes couches d'une application
n-tiers
 Mettre en place une façade Web REST et MVC
 Assurer la persistance de vos données
 Sécuriser votre application avec Spring Security
 Coupler Spring à d'autres technologies de l'écosystème Java JEE
 Objectif : savoir réaliser une application Spring de bout en
bout
 En comprenant correctement le fonctionnement du Framework
 Spring étant très homogène, cela vous permettra également de
comprendre rapidement les parties de Spring que nous ne verrons
pas
 Cours théorique (40%) et pratique (60%)
2
Environnement de travail - Versions
utilisées
 Ce cours est basé sur les versions suivantes
 Eclipse Mars comme environnement de développement
 Spring Framework 4.2
 Jdk 8
 JPA 2.1
 Hibernate 5
 Maven 3.3.9 comme gestionnaire des dépendances et de la
configuration
 HSQLDB 2.3
 Et d’autres Frameworks au fur et à mesure de
l’avancement du cours

3
Plan de la formation
 Introduction à Spring
 Spring Core
 Spring Aop
 Accès aux données – Intégration l’écosystème J2EE
 Spring MVC
 Spring Webservices
 Spring Security
 Spring logging et test
 Spring batch

4
Introduction à Spring

5
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
6
Couplage fort
 Quand une classe A est liée à une classe B, on dit que la classe A est
fortement couplée à la classe B.
 La classe A ne peut fonctionner qu’en présence de la classe B.
 Si une nouvelle version de la classe B (soit B2), est créée, on est
obligé de modifier dans la classe A.
 Modifier une classe implique:
 Il faut disposer du code source.
 Il faut recompiler, déployer et distribuer la nouvelle application aux
clients.
 Ce qui engendre du travail supplémentaire au niveau de la maintenance
de l’application

7
Couplage fort (2)
 Dans l’exemple précédent, les classes A et B sont liées par un
couplage fort.
 Ce couplage fort n’a pas empêché de résoudre le problème
au niveau fonctionnel.
 Mais cette conception nous ne a pas permis de créer une
application fermée à la modification et ouverte à l’extension.
 En effet, la création d’une nouvelle version de la méthode
getValue() de la classe B, va nous obliger d’éditer le code
source de l’application aussi bien au niveau de A et aussi B.
 De ce fait nous avons violé le principe « une application doit
être fermée à la modification et ouverte à l’extension»
 Nous allons voir que nous pourrons faire mieux en utilisant le
couplage faible.

8
Couplage faible
 Pour utiliser le couplage faible, nous pouvons utiliser les interfaces.
 Considérons une classe A et une classe B qui implémente une interface IB.
 Si la classe A est liée à l’interface IB par une association, on dit que le
classe A et la classe B sont liées par un couplage faible.
 Cela signifie que la classe A peut fonctionner avec n’importe quelle classe
qui implémente l’interface IB.
 En effet la classe A ne connait que l’interface IB. De ce fait n’importe quelle
classe implémentant cette interface peut être associée à la classe A, sans
qu’il soit nécessaire de modifier quoi que se soit dans la classe A.
 Avec le couplage faible, nous pourrons créer des application fermée à la
modification et ouvertes à l’extension.

9
Couplage faible (2)
 Exemple : dans un Framework multicouches comment
donner la possibilité aux développeurs de choisir l’orm à
utiliser sans perturber le code qu’ils ont déjà écrit:

10
Réutilisation du code
 Principe de conception orienté objet dont l’objectif est
d’écrire du code générique une fois et pouvoir l’utiliser
plusieurs fois
 Exemple de classe AbstractProperties qui contient le code de
chargement d’un fichier properties.
 Plusieurs classes filles BusinessPorperties, ConfigProperties qui
chacune accède à un fichier différent
 Les classes filles utilisent le code de la classe mère pour l’accès
à leurs fichiers.
 Avantages visibles par exemple dans le cadre des Frameworks
de développement:
 Productivité : moins de code à retaper
 Fiabilité : le code écrit une fois et testé devient fiable

11
Réutilisation du code (2)
 Cela se construit de façon continue pendant les
développements
 Dés qu’un bout de code ou une façon d’accéder à des
modules se répète
 Construire class « utilité » ou classe abstraite par
exemple

12
Séparation des responsabilités (separation
of concerns)
 Principe de modularité, consiste en la division d’un programme
informatique en plusieurs sections s’occupant chacune d’une
fonctionnalité précise qui sera isolée des autres sections
 Ces sections communiquent entre elle avec un minimum de
couplage pour garder leurs indépendance et évolutivité
 Implémentation avec l’utilisation des principes de l’orienté objet :
 Encapsulation des fonctionnalités dans des objets
 Exposition d’interface – définition de contrat d’interface
 Décomposition du code en plusieurs couches (client, serveur)
 Utilisation de designs patterns
 SOA, WebServices
 Introduction des Frameworks comme des cadre de développement
de briques bien spécifiques et isolées (présentation, service, Accès
au données..) qui permettent un couplage faible et une réutilisation
de code éprouvé

13
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, Proxy, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
14
Design pattern
 Bonne pratique de conception éprouvée qui répond à
une problématique conceptuelle précise
 Capitalisation sur l’expérience de la communauté des
développeurs
 Réutilisation d’élément conceptuel et de code. Dans la plus
part des cas l’implémentation est aussi fournie
 Garantie de plus de fiabilité dans les développements
 Gain de productivité
 Catégories de design patterns :
 Les modèles de création : Factory, singleton..
 Les modèles de structure : Façade, Proxy..
 Les modèles de comportement : Command, state..

15
Pattern Singleton
 Sert à contrôler le nombre d’instance (en mémoire) d’une
classe
 Classe sans état (pas d’attributs d’instance)
 Effectuant les mêmes traitements
 Dans les exemples précédents DAOFactoryEntity et
BusinessProperties sont des cas typiques d’un singleton
 Intérêt : prévenir l’instanciation d’objet inutile
 Différence par rapport à une classe statique :
 Le singleton bénéficie des avantages OO (Héritage, implémentation
d’interface..) alors que les méthodes statiques ne le peuvent pas
 Le singleton est une instance (objet) alors que les méthodes
statiques sont invoqués sur l’objet class
 Le singleton peut être initialisés à la première invocation alors que
les classes statiques sont initialisées au lancement de l’application

16
Pattern Singleton (2)
 Implémentations :
 Constructeur privé,
 Attribut stockant l’unique instance,
 Méthode statique qui renvoie l’unique instance
 Plusieurs variantes :
 Instanciation au niveau de la définition de l’attribut X instance
= new X()
 Instanciation (lazy) au niveau de la méthode statique qui vérifie
que l’attribut n’est pas encore instancié et l’instancie le cas
échéant avant de le renvoyer :
 Problème d’accès concurrent qui risque d’occasionner l’instanciation
de deux objets
 Solution  méthode synchronized ou utilisation de ThreadLocal

17
Pattern Factory
 Sert à construire des objets et les renvoyer aux
demandeurs les déchargeant ainsi de les créer
directement et être fortement couplés (les demandeurs)
avec eux

 Peut être implémenté aussi avec une interface au lieu de


la classe abstraite Parent
18
Pattern Proxy
 Permet d’encapsuler un objet à protéger ou dont le cycle de vie est
différent des objets susceptibles de l’appeler :
 Faire des contrôles avant d’appeler l’objet à protéger
 Exécuter un code qui va récupérer l’objet encapsulé depuis un autre
conteneur distribué (RPC, Corba)
 Exemples :

 La classe ProxySubject :
 implémente la même interface que RealSubject afin de pouvoir s’y
substituer
 Embarque dans la méthode doSomething des traitements de
récupération de l’objet RealSubject ou de contrôle de l’appel ou autre
traitement.

19
Inversion of Control ou encore Dependancy
Injection
 Dans tous les exemples précédents, il semble qu’il manque une
pièce pour compléter l’image globale :
 Dans le dernier diagramme de classe, comment une instance de
ProxySubject est instanciée et utilisée au sein de Client ?
 En général un objet Client va instancier un objet ProxySubject ou
déléguer à une classe Factory de le faire pour lui et lui renvoyer une
interface Subject
 Dans les deux cas, pour que l’interaction s’opère ces objets ont
besoin d’être instanciés et présentés les uns aux autres dans ce qui
ressemble à une scène où ils vont interagir
 Conséquence : un des objets s’occupe de la création d’un autre dans
son code directement  couplage fort même si déplacé de
l’appelant à une entité intermédiaire
 Le mieux c’est qu’une autre entité s’en occupe; qui soit
indépendante des deux parties
20
Inversion of Control ou encore Dependancy
Injection (2)
 C’est le but de l’injection de dépendance qui va déléguer
la création et la présentation aux appelants d’un objet, à
une entité extérieure de toute l’interaction afin de
réduire le couplage
 C’est le principe de base autour duquel a été fondé
Spring :
 Une grande Factory qui créé des objets et les injecte là où ils
doivent être utilisées
 Nécessite de la configuration (fichiers ou annotations)
 Utilise l’introspection dans Java pour l’instanciation dynamique
des objets en question

21
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, Proxy, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
22
Architecture n tiers J2EE
 Découpage logique pour implémenter la séparation des
responsabilités sur plusieurs couches ou tiers (serveur distinct ou
pair distinct)
 Client, serveur d’application, serveur de base de données
 Au niveau du serveur applicatif découpage en couche logique,
Application, Service, Domaine, Persistence..

23
Design applicatif en couche emboitée
 Dans cet exemple, les couches en amont appellent les
couches successives jusqu’à la couche de persistance
Presentation
utilise

Application
utilise

ServiceAccess
utilise

Service
utilise

DataAccess
utilise

Persistence

24
Modularité
 Chaque couche :
 s’occupe d’une partie bien distincte dans la chaine de réalisation des
traitements applicatifs
 appelle les couches en amont et communiquent les réponses aux
couches en aval
 D’une application à l’autre on détecte des patterns qui se
répètent qui ne dépendent pas des fonctionnalités de
l’application
 D’où l’intérêt de capitaliser sur ces traitements pour les
inclure dans des Frameworks réutilisables, éprouvés et efficace
 Se concentrer sur les traitements métier de l’application à
développer
 Spring est un Framework puissant qui a été développer dans
cette optique et qui a la particularité de proposer plusieurs
modules qui s’étalent sur plusieurs couches

25
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, Proxy, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
26
Histoire de Spring
 Octobre 2002 Rod Johnson publie son livre
«Expert One-on-One J2EE Design and
Development», dans lequel il propose du code, qui
va devenir plus tard le framework Spring
 Mars 2004 Spring 1.0 sort sous licence Apache 2.0
 2005 Spring devient populaire, en particulier en
réaction par rapport aux EJBs 2.x
 2006 Spring gagne un «Jolt Productivity Award», ce
qui assoit sa notoriété
 2007 Sortie de Spring 2.5, avec support des
annotations
 2009 Achat de SpringSource par VMWare (420
M$)
 2013 Création de Pivotal, une joint venture entre
VMWare et EMC Corporation
27
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, Proxy, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
28
Qu’est ce que Spring

 Un Framework Java :
 Un conteneur IoC
 Un ensemble de projets
 Une communauté et une société
 Open Source : licence Apache 2.0

29
Un Framework Java
 Framework : un cadre de développement
 Contient des «bonnes pratiques»
 Permet d’éviter de recoder des classes utilitaires
 Permet de se focaliser sur le métier :
 Spring fournit la «plomberie» : le socle technique
 Les développeurs peuvent se concentrer sur le code métier (le vrai
travail)
 A l’origine orienté Java et Java EE
 Aujourd’hui d’autres implémentations existent : .NET et
Python

30
Un conteneur IoC
 IoC == Inversion of Control
 Le «principe d’Hollywood» : Ne nous appelez pas, nous vous rappellerons
 Permet d’avoir des composants «faiblement couplés»
 Améliore la qualité du code
 Facilite les tests
 La principale forme d’IoC : «l’injection de dépendances» (ou DI)
 Exemple d’injection de dépendances :
 A l’exécution, une instance de «TrainingRepository» est injectée dans
l’instance de «TrainingService»
 Spring se charge de cette injection

31
Quel intérêt ?
 Cette injection permet de «découpler» les classes
 Donne une architecture logicielle plus souple, plus simple à
faire évoluer
 Facilite les tests
 Permet à Spring «d’enrichir» les instances d’objets
injectés
 Les objets injectés peuvent être modifiés par Spring
 Par exemple, les rendre transactionnels ou sécurisés
 Ceci est permis grâce à la Programmation Orientée Aspect
 C’est ainsi que Spring peut fournir la «plomberie technique»,
sans que le développeur n’ait à faire quoi que ce soit

32
Un ensemble de projets
 Spring est un projet principal avec de nombreux sous-
projets
 On parle de «Spring Core» pour le projet principal
 Spring MVC
 Spring Web Flow
 Spring Security
 Spring Batch
 Spring Integration
 Spring Web Services
 Spring Roo
 ...

33
Un ensemble de modules et sous projets

34
Une communauté et une société
 Spring est à la base un projet communautaire
 Mais le projet est «sponsorisé» par VMWare
 Les développeurs principaux travaillent pour VMWare
 La propriété intellectuelle appartient à VMWare
 Certains sous-projets ont leurs propres particularités (pas de
«sponsorisation» par VMWare, ou sponsorisation» en commun avec
d’autres sociétés)
 Vous pouvez participer, et le code sera toujours Open Source, mais
la gestion du projet reste maîtrisée par VMWare
 Spring et ses sous-projets sont sous licence «Apache 2.0»
 Licence certifiée Open Source par l’OSI
 Non virale, et donc «business friendly», par opposition à la GNU GPL
 Vous pouvez donc utiliser Spring, l’étendre et le modifier sans aucun
souci
 Par contre ce n’est pas un projet de la fondation Apache, et il n’a pas
son modèle de gouvernance et de développement
35
Une communauté et une société (2)
 Le code de Spring est disponible gratuitement, à plusieurs
endroits
 Officiellement : le site de Spring (http://spring.io/)
 Vous n’êtes pas forcés de vous inscrire
 Garantie d’avoir le «bon» code (sans modification par des tiers
malveillants)
 Maven : les versions stables sont sur le repository central, les
autres sur le repository de Spring
 GitHub
 Spring Core
 https://github.com/spring-projects/spring-framework
 Sous-projets Spring
 https://github.com/spring-projects

36
Agenda
 Rappel des principes de l’orienté objet
 Couplage faible Vs couplage fort
 Réutilisation du code
 Séparation of concerns (séparation des responsabilités)
 Design patterns
 Factory, Singleton, Proxy, IoC (DI)
 Architecture n tiers J2EE
 Design en couches emboitées
 Modularité
 Histoire de Spring
 Qu’est ce que Spring
 Que peut on faire avec Spring
37
Que peut on faire avec Spring
 Spring fournit un «cadre de travail»
 Une aide, essentiellement pour simplifier les aspects techniques des
projets
 Des patterns d’architecture prédéfinis
 Spring fait «la plomberie», à vous de coder la partie métier du projet
 Les sous-projets traitent de problématiques techniques plus
spécifiques
 Réaliser un batch, un Web Service, sécuriser son application...
 Ils fonctionnent tous sur le même principe, et sont par conséquent
très faciles à apprendre une fois qu’on maîtrise le «Core»
 Rien n’est obligatoire, tout est configurable
 Vous ne prenez que ce qui vous plaît
 Très souple et sans contrainte

38
Que peut on faire avec Spring (2)
 Spring n’est pas un serveur d’applications
 Il peut fonctionner sans serveur d’applications (application
«stand alone», par exemple un batch)
 Il peut fonctionner à l’intérieur d’un serveur d’applications
 Il peut alors utiliser les fonctionnalités du serveur, pour en simplifier
ou en abstraire le fonctionnement
 Il peut également remplacer certaines fonctionnalités du serveur, si on
juge que Spring propose une meilleure alternative
 Généralement Spring est utilisé conjointement à un
serveur d’applications léger : Tomcat ou Jetty
 Peu d’intérêt à utiliser Spring dans un serveur Java EE
«complet»
 Mais Spring fonctionne également parfaitement avec Weblogic,
WebSphere, Glassfish, etc
39
Que peut on faire avec Spring (3)
 Spring est aujourd’hui le Framework Java n°1 en
entreprise
 Dans tous les secteurs d’activité
 Dans tous les pays
 Tous les Frameworks concurrents ont plus ou moins
disparu (Avalon, HiveMind, PicoContainer...)
 Les EJB 3.x s’inspirent beaucoup de Spring
 Java EE revient progressivement dans la course contre Spring

40
Spring Core

41
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : XML, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

42
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : XML, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

43
Injection de dépendance
 A la base, il s’agit simplement d’injecter un objet dans un
autre :

anotherService doit être injecté

public class MyServiceImpl {


private AnotherService anotherService;
}

 Plusieurs façons de le faire

44
Injection par setter
 Spécifier une méthode setter (à la convention JavaBean)
 Le conteneur Spring va appeler cette méthode pour
injecter le bean

public class MyServiceImpl {


private AnotherService anotherService;

public void setAnotherService(AnotherService anotherService) {


this.anotherService = anotherService
}
}

45
Injection par constructeur
 Spécifier un constructeur prenant en paramètre la classe de
l’objet à injecter
 Le conteneur Spring va construire l’objet MyServiceImpl en lui
passant un objet AnotherService

public class MyServiceImpl {


private AnotherService anotherService;

public MyServiceImpl(AnotherService anotherService) {


this.anotherService = anotherService;
}
}

46
Injection dans un champ
 3ème méthode : Spring injecte directement dans le champ
 Méthode «magique» : en fait les champs «private» en Java peuvent être
modifiés (si vous venez d’avoir un cours sur Java, on vous a menti)
 De plus en plus populaire car la méthode la plus simple

public class MyServiceImpl {


private AnotherService anotherService;

47
Quelle méthode pour quelle utilisation
 Injection par setter
 Respecte la convention JavaBeans (sans grand intérêt)
 Héritage automatique
 Plus clair que par constructeur
 Permet d’avoir des dépendances optionnelles (l’objet va être null si la
méthode setter n’est pas appelée à cause d’un souci de config par
exemple)
 Injection par constructeur
 Permet d’avoir des objets immutables
 Oblige à avoir toutes les dépendances correctement définies (c’est au
moment de l’instanciation de l’objet que l’indépendance est injectée et
non pas après la création comme c’est le cas pour l’injection par setter)
 Plus concise que par setter
 Injection par champ
 Mêmes qualités que par constructeur
 Encore plus concise

48
Quelle méthode pour quelle utilisation (2)
 Vous pouvez mélanger les 3 types d’injection
 Utilisez le plus simple en fonction de votre existant
 L’injection par champ est la plus efficace pour le
développement
 Utilisez l’injection par setter pour les dépendances
optionnelles
 Le plus important est d’être homogène
 Si vous injectez votre data source de 3 manières différentes,
personne ne va rien y comprendre !
 Il est important de mettre en place des règles à ce sujet dès le
début du projet

49
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : XML, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

50
IoC dans Spring

 Un Application Context (une des implémentations de l’interface


org.springframework.context.ApplicationContext) représente le conteneur Spring : il est
chargé de démarrer les beans, de les injecter, de les gérer, de les détruire
 Il en existe plusieurs sortes : WebApplicationContext pour les applications Web ou
ClassPathXmlApplicationContext pour les applications Standalone par exemple
 Le rôle de cette classe est de prendre vos objets et votre configuration, et de faire
fonctionner l’ensemble

51
Instanciation du IoC container
 Prend le paramètre non du fichier de configuration « application-context.xml »
 Ce fichier contient :
 Des éléments de configuration du container lui-même (pour lui dire d’utiliser les
annotations par exemple)
 Les éléments de configuration de tous les beans de l’application qu’on veut faire gérer par
Spring
 Import d’autres fichiers de config : la bonne pratique voulant qu’il y ait des fichiers de
configuration séparés par couche ou pas module de l’application

ApplicationContext ctx =
new ClassPathXmlApplicationContext(
"application-context.xml");

 ApplicationContext peut être utilisée pour récupérer une instance d’un bean :
tx.getBean("myBean")
 Mais ce n’est pas recommandé pour garantir l’indépendance du code applicatif des
artefacts de Spring
 En général il n’y pas besoin de faire appel à ces méthodes

52
Les objets métier
 Ces objets métier sont des objets Java simples
 «POJO» pour Plain Old Java Object, un «bon vieil objet Java» en
français
 Pas forcément des JavaBeans, mais c’était le cas à l’origine, c’est
pourquoi on parle de «Beans» par habitude
 Généralement ils implémentent une interface métier
 Cela permet de découpler les objets (on injecte une interface, pas
une implémentation), et donc d’avoir une architecture plus souple
 Cela simplifie l’écriture des tests unitaires (c’est en fait une
conséquence du point précédent)
 C’est également intéressant au niveau technique, car Spring utilise
cette particularité pour injecter une implémentation «enrichie» :
c’est ainsi que fonctionne la Programmation Orientée Aspect, dans sa
version la plus simple

53
Exemple d’objet métier

54
Exemple de configuration XML
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation= "http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd" >
<!-- injection par setter -->
<bean id="todoService" class="example.TodoServiceImpl" >
<property name="userService" ref="userService" />
</bean>
<!-- injection par constructeur -->
<bean id="userService" class="example.UserServiceImpl" >
<constructor-arg ref="userRepository" />
</bean>
</beans>

55
Exemple de configuration XML (2)
 Utilise les «namespaces» XML
 Spring fournit une dizaine de namespaces spécialisés : beans,
transactions, sécurité, Programmation Orientée Aspect, etc...
 L’import de ces namespaces permet de considérablement
simplifier cette configuration
 Propose l’auto-complétion et fournit la documentation
 Fonctionne avec tout éditeur XML
 Crée automatiquement des ensembles de Beans Spring
 Un Bean a un ID (unique) et une classe (son
implémentation)
 Les Beans sont injectés
 Par Setter avec <property name="" ref=""/>
 Par constructeur avec <constructor-arg ref=""/>

56
Exemple de configuration par
annotations
@Component
public class TodosServiceImpl implements TodosService {
@Autowired
private UserService userService;

private AnotherService anotherService;

private YetAnotherService yetAnotherService;

@Autowired
public void setAnotherService (AnotherService anotherService) {
this.anotherService = anotherService;
}

@Autowired
public TodosServiceImpl (YetAnotherService yetAnotherService) {
this.yetAnotherService = yetAnotherService;
}
public Collection<Todo> findAssignedTodos () {
..
}
}

57
Exemple de configuration par
annotations (2)
 Il faut préciser dans le fichier XML que l’on veut utiliser
les annotations :
<beans xmlns="...">
<context:component-scan base-package= "example.test" />
</beans>

 Les Beans annotés @Component sont automatiquement


créés
 Les Setters, constructeurs et champs annotés avec
@Autowired sont automatiquement injectés

58
Comment Spring trouve-t-il les
dépendances ?
 Le plus évident : par nom
 Pour injecter un Bean nommé «userService», Spring recherche le
Bean qui a cet ID
 C’était notre exemple de configuration XML
 Le plus concis : par type
 On ne nomme pas le Bean à injecter : Spring recherche alors son
type
 Comprendre type au sens Java : Spring recherche alors quel Bean est
de ce type là (même classe, ou bien implémentant cette interface)
 Si Spring en trouve un, il l’injecte, et tout se passe bien
 S’il n’en trouve pas on a alors une Exception, et Spring ne peut pas se
lancer
 S’il en trouve plusieurs, on a également une Exception
 C’était notre exemple de configuration par annotations

59
Configuration : Annotations, XML, Java ?
 Il existe en fait 3 manières de configurer Spring
 XML : méthode «classique», très souple et très puissante
 Essentielle à connaître
 Convient très bien à la configuration dite «d’infrastructure»
 Annotations : depuis Spring 2.5
 Plus rapide à utiliser
 Plus simple : ne convient qu’à de la configuration «métier»
 Java : depuis Spring 3.0
 Permet de coder en Java quelque chose de similaire à la configuration
XML
 Plus puissant (c’est du code, on peut faire ce qu’on veut)
 Moins simple à modifier, en particulier pour de la configuration «d’
infrastructure»
 Moins répandu

60
Configuration : Annotations, XML, Java ? (2)
 Configuration «métier»
 Les Beans codés par les développeurs du projet
 Représentent des objets et des méthodes métier
 Changent peu souvent
 Ne sont pas sensibles aux environnements : ce sont les mêmes
objets en développement, test et production
 Exemple : Beans «transfert d’argent» ou «gestion des utilisateurs»
 Configuration «d’infrastructure»
 Typiquement fournie par Spring ou un Framework complémentaire
 Représente des objets et des méthodes techniques
 Change souvent
 Est sensible aux environnements : objets et configuration différents
en fonction de l’environnement (développement, test, production)
 Exemple : une data source, un gestionnaire de transaction

61
Configuration : Annotations, XML, Java ? (3)
 Configuration typique :
 Des fichiers «d’infrastructure»
 Plusieurs fichiers, découpés en fonction de leur périmètre fonctionnel
 Exemple : un fichier pour configurer la sécurité, un fichier pour
configurer la base de données
 Des annotations dans les Beans de la couche «métier»
 Les développeurs gagnent ainsi en temps de développement
 Facilite le refactoring

62
Configuration XML
 Deux volets de configuration :
 Définition des beans que le conteneur doit gérer
 Définition de l’injection des dépendances pour ces beans
 Définition d’un bean :
 class : nom complet de la classe du bean (incluant le package)
 name : nom par lequel le bean sera référencé dans d’autres définitions de bean
 scope : scope du bean qu’on va voir un peu plus loin
 Arguments de constructeur : argument à passer au constructeur du bean lors de
sa création (injection de dépendance)
 Propriétés : attribut du bean nécessitant initialisation ou référençant d’autres
bean (injection de dépendance)
 Autowiring mode : paramétrage du mode autowiring qui autorise Spring à
trouver et injecter automatiquement les beans référencés dans le bean actuel
 Lazy-initialization mode : mode d’initialisation en retard
 Initialization / destruction méthodes : pour respectivement, ajouter du
comportement juste après la création du bean et juste avant la destruction du
bean

63
Nommage des beans
 Attributs id ou name de la balise bean
 Sera utilisé pour référencer le bean dans d’autres
définition de bean ou dans un appel programmatique du
container pour obtenir une instance du bean
 Balise <alias> permet d’ajouter des alias à un bean déjà
définie
<beans xmlns="...">
<bean id="myBean" class= "example.test.MyBean" />
<bean name="mySecondBean" base-package= "example.test.SecondBean" />
<bean name="myThirdBean, thirdBean " base-package= "example.test.ThirdBean" />
<alias name="myBean" alias= "theBean" />
<bean id="consumer" name="theConsumer" class= "example.test.Consumer" >
<property name="aBean" ref= "theBean" >
</bean>

</beans>

64
Nommage des beans (2)
 Tout Bean est référencé dans le conteneur Spring avec un nom
unique
 Si deux Beans ont le même nom, vous aurez une Exception au démarrage
 Ce nom est libre, mais par convention on utilise généralement le
nom de la classe (ou de l’interface implémentée), en CamelCase, en
commençant par une minuscule
 «dataSource», «monServiceMetier», «entityManager»
 L’homogénéité du nommage dans l’application est important
 Bien nommer les Beans aide pour la Programmation Orientée Aspect.
 Par exemple, pour sélectionner tous les Beans dont le nom finit par
«Metier».
 A l’origine, en configuration XML, ce nom était l’ID XML du Bean
 Garantit l’unicité dans un fichier XML (norme XML)
 Mais cela interdisait certains caractères («/») ainsi que de donner deux
noms différents au même Bean (il fallait utiliser un alias)

65
Instanciation des beans
 Création des beans; utilisation de l’attribut class de deux
façons possibles :
 Spécifier le nom complet de la classe pour permettre l’instanciation
du bean en utilisant la « reflection » java (un peu comme utilisant
new ):
 Spécifier la classe factory contenant une méthode (statique ou
d’instance) qui permettra de créer le bean
public class ClientService {
private static ClientService clientService =
new ClientService();
private ClientService() {}
<beans xmlns="...">
public static ClientService createInstance() {
<bean id="myBean" class= "example.test.MyBean" /> return clientService;
<bean id="clientService" class= }
"example.test.ClientService" }
factory-method= "createInstance" />
<bean id="clientService2" factory-bean= "myBean" public class MyBean {
private static ClientService clientService =
factory-method= "createInstance" /> new ClientServiceImpl();
private MyBean() {}
</beans>
public ClientService createInstance() {
return clientService;
}
}

66
Instanciation des beans (2)
 Création des beans par constructeur :
 Passer des arguments au constructeur en utilisant la balise
« constructor-arg » qui permet de passer des références à d’autres
beans ou des paramètres de types Java :

<bean id="exampleBean" class="examples.ExampleBean">


<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>

<constructor-arg type="int" value="1"/>


 </bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

 Création des beans par méthode :


 La même balise est utilisée pour passer des paramètres

67
Injection de beans
Injection de dépendance par constructeur

<bean id="exampleBean" class="examples.ExampleBean">


<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg>
<!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/>

<constructor-arg type="int" value="1"/>


</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/> Injection de dépendance par setter

<bean id="consumer" class="examples.test.Consumer">


<property name="myBean" ref=" yetAnotherBean "/>
</bean>

68
Injecter autre chose que des beans
 Spring ne se limite pas à l’injection de Beans
 Vous pouvez injecter :
 Des objets simples : int, String ...
 Des objets complexes : properties, fichiers ...
 Des ensembles d’objets : List, Set, Map ...
 Cela permet d’avoir des configurations d’infrastructure
très complètes et très souples
 De par la complexité de ces configurations, elles sont
essentiellement réalisées via XML ou en Java (pas en
annotations)

69
Injection de types primaires
 Vous pouvez injecter n’importe quel type primaire dans
un Bean Spring
 Spring va automatiquement convertir la chaîne de texte dans le
bon type

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method= "close">


<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/tudu" />
<property name="user" value="system"/>
<property name="password" value="manager"/>
<property name="initialPoolSize" value="10"/>
<property name="minPoolSize" value="10"/>
<property name="maxPoolSize" value="30"/>
</bean>

70
Injection d’objets complexes
 Spring est également capable d’injecter des objets plus
complexes
 Fichiers, properties, patterns (expressions régulières), URL...
 Ce mécanisme peut également être étendu à vos propres
objets

<bean id="fileBean" class="example.FileBean" >


<property name="fileToInject" value="classpath:test/monfichier.txt" />
</bean>

71
Injection de Collections
 On peut également injecter des collections Java
 List, Set, Map…
<property name="emails">
<list>
<value>formation@premsoft.ma </value>
<value>contact@premsoft.ma </value>
<value>recrutement@premsoft.ma </value>
</list>
</property>

<property name="emails">
<map>
<entry key="formation" value="formation@premsoft.ma" />
<entry key="contact" value="contact@premsoft.ma" />
<entry key="recrutement" value="recrutement@premsoft.ma" />
</map>
</property>

72
Inner beans
 Même principe que les inner class en Java
 Un inner Bean est déclaré à l’intérieur d’un autre Bean
 Il est injecté dans ce deuxième Bean
 Seul ce deuxième Bean peut le «voir»
 Clarifie la configuration
 Il n’y a pas de limite : on peut faire des inner Beans de inner Beans…

<bean id="beanA" class="example.BeanA" >


<property name="beanB">
<bean class="example.BeanB" >
<property name="prop1" value="ABC"/>
<property name="prop2" value="123"/>
</bean>
</property>
</bean>

73
Héritage entre les beans
 Même principe que l’héritage Java
 On peut surcharger des classes ou des properties
 On peut même faire des Beans abstraits

<bean id="parentBeanDefinition" abstract="true">


<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="childBean" class="example.childBean“ parent="parentBeanDefinition" init-method=
"initialize" >
<property name="name" value="override" />
<!-- l’âge est hérité, il est donc de «1» -->
</bean>

74
Configuration par XML
 Possibilité de définir la configuration sur plusieurs fichiers XML
 Chaque fichier s’occupe d’un module ou d’une couche particulière :
 persistence-context.xml
 properties-context.xml
 ..
 Importer ces fichiers dans un fichier de config principal qui sera le point central de la configuration A
vérifier
<beans>
<import resource="persistence-context.xml"/>
<import resource="properties-context.xml"/>
..
</beans>

<beans>
<bean id="eco-context" class="org.springframework.context.support.ClassPathXmlApplicationContext">
<constructor-arg>
<list>
<value>classpath:com/structis/ecoproduits/server/config/spring/properties-context.xml</value>
<value>classpath:com/structis/ecoproduits/server/config/spring/persistence-context.xml</value>
</list>
</constructor-arg>
</bean>
</beans>

75
Configuration par annotations
 Utiliser des annotations de Spring pour :
 Indiquer les beans que Spring doit gérer
 Indiquer les dépendances à injecter dans ces beans
 L’annotation de classe @Component permet d’indiquer
que le bean doit être géré par Spring :
 D’autres annotations existent pour ce faire (héritent toutes de
@Component) :
 @Repository pour des beans dans la couche de persistance
 @Service pour des beans dans la couche service
 @Controller pour des beans dans la couche de présentation
 Certaines annotations introduisent un traitement spécifique
comme @Repository : conversion des exceptions
 Les autres permettent juste un marquage plus fin des beans

76
Indiquer les beans que Spring doit gérer
 Spring va scanner le classpath pour trouver les beans à
créer :

<beans>
<context:component-scan base-package="org.example">
<context:include-filter type="regex" expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
</context:component-scan>
</beans>

@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}

77
Définition d’un bean par annotation
 Les annotations peuvent définir certains attributs du bean que
nous avons vu dans le cas de la configuration par Xml :

@Lazy
@Scope("prototype")
@Service("myMovieLister")
public class SimpleMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}

@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}

78
Définition d’un bean par annotation (2)
 On retrouve aussi la possibilité de définir des méthodes
d’instanciation d’autres beans : Ce n’est pas recomm
du mode de configur
que dans ce cas elle
dég
@Component
public class FactoryMethodComponent {

@Bean
@Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}

public void doWork() {


// Component method implementation omitted
}
}

 Par contre on ne peut pas définir des détails sur l’initialisation


des paramètres de constructeurs par exemple

79
Injection de dépendance par annotation
 Utiliser l’annotation @Autowired sur des attributs, des
méthodes setter ou encore sur le constructeur comme on l’a
vu dans un exemple précédent
 Il est aussi possible d’injecter tous les beans d’un certains type
automatiquement dans un attribut collection ou tableau

public class MovieRecommender {

@Autowired
private MovieCatalog[] movieCatalogs;
// ...
}

 Utiliser @Autowired sur un attribut de type interface pour


injecter le bean qui l’implémente (à condition qu’il n’y en ai
qu’un)
80
Injection de dépendance par annotation (2)
 Qu’est ce qui se passe si plusieurs beans correspondent à
la définition d’injection de dépendance automatique :
 Exception
 Solution : définir un bean prioritaire sur les autres avec
l’annotation @Primary
 Ou encore
 ajouter sur les définitions des beans concurrents des qualifier
différents @Qualifier("main"), @Qualifier("secondary")
 Et l’utiliser dans la définition de l’injection de dépendance pour
sélectionner celui qui sera injecté :
 @Autowired @Qualifier("main")

81
Injection de dépendance par annotation (3)
 Utiliser l’annotation @Resource en place de Autowired :
 @Resource ou
 @Resource(name="myMovieFinder")
 A la différence de @Autowired, @Resource va chercher
à résoudre la dépendance par nom :
 Attribut name si fournit
 Déduire le nom du bean à injecter du nom de l’attribut ou de
la méthode sur lesquels il est positionné
 Sinon il va essayer de résoudre le bean par type

82
Annotations Standard de Java
 Il est possible d’utiliser des annotations standards de Java
(javax.inject) :
 @Inject est l’équivalent de @Autowired
 @Named est l’équivalent de @Component
 Intérêt :
 Pas d’intrusion de dépendance à Spring dans le code

83
Configuration par code java
 La configuration est contenu dans des classes de Java qui utilisent l’annotation de
classe @Configuration
 Une telle classe va contenir l’équivalent des définitions Xml de bean sous forme de
méthodes annotée avec @Bean
@Configuration
public class AppConfig {
<beans>
<bean id="myService"
@Bean
class="com.acme.services.MyServiceImpl"/>
public MyService myService() {
</beans>
return new MyServiceImpl();
}

 Nécessite l’utilisation d’un ApplicationContext particulier pour supporter cette


configuration :

public static void main(String[] args) {


ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}

84
Définition des beans à gérer par le
conteneur
 Indiquer à Spring de scanner le classpath pour trouver les
beans à créer :
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}

 Comportement identique à la config Xml équivalente


pour détecter les beans utilisant l’annotation
@Component

85
Définition des beans à gérer par le
conteneur (2)
@Configuration
public class AppConfig {

@Bean(initMethod = "init“, name = "myFoo")


@Scope("prototype")
public Foo foo() {
return new Foo();
}

@Bean(destroyMethod = "cleanup", name = { “bar", “myBar", “theBar" })


public Bar bar() {
return new Bar();
}

@Bean
@Primary
public Bar bar2() {
return new Bar();
}

@Bean
public Demo demo(@Value("#{foo.age}") String age) {
Demo dem = new DemoImpl(age);
return dem
}

86
Injection des dépendances
@Configuration
public class AppConfig {

@Bean
public Foo foo() {
return new Foo();
}

@Bean
public Bar bar() {
Bar bar = new BarImpl();
bar.setFooInstance(foo());
return bar;
}

@Bean
public Demo (Foo foo) {
return new DemoImpl(foo);
}

@Bean
public Scene () {
return new SceneImpl(foo());
}
}

87
Injection d’objet complexe
@Configuration
public class AppConfig {

@Bean
public Foo foo() {
Foo foo = new FooImpl();
List<String> emails = new ArrayList<String>();
emails.add(“formation@premsoft.ma”);
emails.add(“contact@premsoft.ma”);
emails.add(“recrutement@premsoft.ma”);
foo.setEmails(emails);
return foo;
}

@Bean
public DataSource dataSource() {
DataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass(“com.mysql.jdbc.Driver”);
dataSource.setJdbcUrl(“jdbc:mysql://localhost:3306/tudu”);
dataSource.setUser(“system”);
..
return dataSource;
}

88
Configuration par code java
 Possibilité de définir la configuration sur plusieurs Classes
Java
@Configuration
public class ConfigA {

@Bean
public A a() {
return new A();
}
}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

@Bean
public B b() {
return new B();
}
}

89
Combiner les configurations Java et Xml
 Il est possible de combiner les deux méthodes de
configuration :
 Inclure la classe définissant la config Java comme un bean défini
dans le fichier Xml de config
 Inclure le fichier Xml comme ressource dans la classe de
configuration Java

90
Config Xml contenant un bean de config
java
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
@Bean
public B b() {
return new B();
}
}

<beans>
<!-- enable processing of annotations such as @Autowired and @Configuration -->
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.ConfigA"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>

 L’annotation @Configuration est meta annotée elle-même par @Component donc la classe qui l’utilise est considéré comme
un bean que le conteneur peut gérer
 L’ApplicationContext dans ce cas là est ApplicationContext ctx = new
ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");

91
Config Java important un fichier Xml de
config
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean
public DataSource dataSource() {
return new DriverManagerDataSource(url, username, password);
}
}

<beans>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
</beans>

 L’ApplicationContext dans ce cas là est ApplicationContext ctx =


new AnnotationConfigApplicationContext(AppConfig.class);

92
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : Xml, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

93
Les scopes des beans
 Par défaut, les Beans Spring sont dits être des
«singletons»
 Ils ne sont instanciés qu’une seule fois par contexte Spring
 Ils ne sont pas de «vrais» singletons : on peut lancer deux fois
la même classe (deux Beans ayant la même implémentation)
 Les Beans sont instanciés et configurés au démarrage du
contexte Spring
 Permet de valider que la configuration est correcte dès le
démarrage de l’application
 Permet de gagner en performance : on ne crée pas un grand
nombre d’instances d’objets pour rien

94
Les beans et le multithreading
 Le fait d’avoir des singletons a un impact en
environnement multithread :
 Les variables d’instance sont partagées entre les threads
 Les beans doivent donc être thread-safe
 S’il faut avoir des valeurs différentes par thread; utiliser un
objet ThreadLocal qui préservera l’état d’un thread à l’autre

95
Singletons et données partagées
 Que faire si vous avez besoin de plusieurs instances du
même Bean ?
 Exemple très fréquent : il contient des données de l’utilisateur
 On peut donner un «scope» à un Bean
 singleton, session, application, request, prototype
 prototype : une nouvelle instance à chaque fois qu’on injecte ce
Bean
 request : le bean est associé à la request http; chaque requête
http aura sa propre instance du bean
 session : le bean est associé à la session http; chaque session
http aura sa propre instance du bean
 application : le bean est associé au scope du ServletContext
96
Point de vigilance
 Que se passe-t-il si on injecte un Bean avec un scope «session» dans un
Bean avec un scope «singleton» ?
 Votre Bean «session» se retrouve injecté une seule fois dans le Bean «singleton»
 Ce sera une variable partagée !
<bean id="todoService" class="example.TodoServiceImpl" >
<property name="userService" ref="userService" />
</bean>
<bean id=" userService " class="example.UserService" scope=“session" />

 Pour éviter cela, il faut plutôt injecter un proxy du bean userService qui lui
va à chaque invocation des ces méthodes déléguer le traitement au bean
userService de la session courante. Pour ce faire, configurer ce Bean
spécialement :

<bean id="todoService" class="example.TodoServiceImpl" >


<property name="userService" ref="userService" />
</bean>
<bean id=" userService " class="example.UserService" scope=“session" >
<aop:scoped-proxy/>
</bean>

97
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : Xml, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

98
Le cycle de vie des beans
 La vie des Beans est gérée par Spring
 C’est Spring qui crée les Beans
 C’est Spring qui les «enrichit» avec la Programmation Orientée
Aspect
 C’est Spring qui les injecte
 C’est Spring qui les détruit (parfois)
 Ce cycle de vie est défini par Spring
 Spring propose également des options de configuration pour
agir sur les Beans, au moment de leur création ou de leur
destruction

99
Etape 1 : Lecture de la configuration
par Spring
 Au démarrage, Spring lit sa configuration
 Dans un fichier XML
 Dans les annotations
 Dans la configuration Java de Spring
 Spring possède alors un modèle mémoire de la
configuration qu’on lui a fournie
 A partir de ce moment, Spring ne différencie plus d’où provient
la configuration
 Il est donc impossible d’avoir un comportement différent
entre une configuration XML et une configuration par
annotation (= en cas de problème, c’est votre configuration qui
est mauvaise)

100
Le BeanFactoryPostProcessor
 Spring propose à ce moment un premier point
d’extension : le BeanFactoryPostProcessor
 Il permet de modifier la configuration des Beans
 Exemple typique : le PropertyPlaceholderConfigurer
 Permet de remplacer des variables (de type ${} ) par des
valeurs externes à l'application, par exemple en provenance
d’un fichier . properties
 Très souvent utilisé, le fichier plat pouvant être modifié
facilement par des administrateurs ou des scripts

101
Exemple de
PropertyPlaceholderConfigurer

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >


<property name="locations" value="classpath:com/foo/jdbc.properties" />
</bean>
<bean id="dataSource" destroy-method= "close“ class="org.apache.commons.dbcp.BasicDataSource" >
<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>

jdbc.driverClassName =org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username =sa
jdbc.password =root

102
Etape 2 : instanciation et injection
des Beans
 Une fois la configuration traitée par Spring, celui-ci va créer les
Beans (scope Singleton) qui ont été définis
 Il crée les Beans en utilisant l’API Reflection de Java
 Il crée les Beans dans le bon ordre
 Il fait normalement l’injection de dépendance après l’instanciation des Beans
 Sauf pour l’injection de dépendance par constructeur
 Attention aux dépendances cycliques dans ce cas (mais elles sont généralement la
preuve d’une mauvaise architecture)
 C’est au moment de l’injection que Spring crée des «proxy» sur les
objets
 Si nécessaire, Spring génère une implémentation «enrichie» des objets
 Ajout des transactions, de la sécurité, etc...
 C’est ainsi que fonctionne la Programmation Orientée Aspect
 Tant que l’injection de dépendance n’a pas eu lieu, les objets ne sont
donc pas prêts à être utilisés

103
Etape 3 : initialisation des Beans
 Une fois tous les Beans créés et injectés par Spring, un
nouveau point d’extension est disponible
 Il ne faut pas utiliser les constructeurs des objets pour faire des
choses complexes, car les Beans ne sont pas encore prêts (l’injection
de dépendance n’est pas encore faite)
 On peut alors initialiser les Beans, avec 3 méthodes
 Une annotation @PostConstruct
 Une configuration XML (attribut «init-method»)
 Une interface à implémenter (InitializingBean)
 La manière recommandée est d’utiliser l’annotation
 Elle est standardisée (JSR 250) : aucune dépendance sur Spring
!
 Elle est simple et peu intrusive

104
Exemple de @PostConstruct

@Service
@Transactional
public class TodosServiceImpl {
@PostConstruct
public void init() {
System. out.println("Bonjour de TodosServiceImpl" );
}
}

 Astuce : c’est un bon moyen pour vérifier si vos Beans


sont bien démarrés

105
Les BeanPostProcessor
 Les phases d’injection de dépendance et d’initialisation
(étapes 2 & 3) peuvent être personnalisées
 Une fois un Bean créé, Spring laisse en fait une chaîne de
BeanPostProcessor modifier l’instance de ce Bean
 Ainsi, le @PostConstruct que nous venons de voir est traité par
un BeanPostProcessor
 Attention, nous avons vu auparavant les
BeanFactoryPostProcessor
 BeanFactoryPostProcessor : manipule les configurations XML des Beans
 BeanPostProcessor : manipule les instances des Beans

106
Etape 4 : Run !
 Une fois les Beans instanciés, injectés et initialisés le
conteneur Spring est prêt à l’emploi
 Il devient accessible (on peut lui demander un Bean via l’API)
 L’application est prête et est démarrée
 C’est dans cette phase que votre application se trouve
99% du temps
 Sauf si elle crashe souvent :-)

107
Etape 5 : Destruction
 On peut demander à Spring de s’arrêter
 Manuellement via l’API
 Lorsque votre conteneur Web s’arrête
 Lors de cette phase d’arrêt, tous les Beans Spring vont
s’arrêter
 Cette phase est optionnelle
 L’application peut tout simplement crasher : dans ce cas cette
phase n’est évidemment pas prise en compte
 Cela ne fonctionne pas pareil pour tous les Beans
 Aucun souci pour les Singletons
 Les Prototypes, par nature, ne sont pas arrêtés par Spring («fire and
forget»)

108
Destruction des Beans
 Même principe que pour l’initialisation, avec 3 méthodes
 Une annotation @PreDestroy
 Une configuration XML (attribut «destroy-method»)
 Une interface à implémenter (DisposableBean)
 L’annotation sera aussi privilégiée

109
Exemple de @PreDestroy
 ATTENTION : pour un Prototype cette méthode ne sera
pas appelée

@Service
@Transactional
public class TodosServiceImpl {
@PreDestroy
public void destroy() {
System. out.println("Au revoir de TodosServiceImpl" );
}
}

110
Un cas particulier : le lazy-loading
 Que faire si vos beans sont lents au démarrage ?
 Un exemple possible : l’initialisation d’un cache
 On ne va pas vouloir suivre le cycle de vie classique : on veut avoir
des Beans uniquement instanciés à la demande : c’est le «lazy
loading»
 C’est généralement une fausse bonne idée :
 Le premier utilisateur à utiliser cet objet va alors subir le chargement
 Il y a toujours un risque de mauvaise configuration : mieux vaut être sûr
du bon lancement de ses Beans au démarrage de l’application
 Une solution : avoir une configuration d’infrastructure différente suivant
votre environnement, et ne faire le lazy loading qu’en développement

<bean id="todoService" class="example.TodoServiceImpl" lazy-init= "true" >


<property name="userService" ref="userService" />
</bean>

111
Résumé sur le cycle de vie des beans
 Ce cycle de vie est toujours respecté par Spring
 Il est prévu à l’origine pour des Singletons, qui sont donc
tous démarrés, injectés et initialisés au démarrage de
l’application
 Cela vous assure d’avoir une configuration fonctionnelle une
fois l’application démarrée
 Cela vous assure aussi d’excellentes performances : on travaille
uniquement avec des Singletons, qui sont déjà prêts à être
utilisés
 Mais dans certains cas particuliers, ce cycle n’a pas lieu au
démarrage de l’application : les beans qui ne sont pas des
Singletons, et les beans utilisant le lazy loading
112
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : Xml, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

113
Démarrer Spring
 Pour démarrer Spring, il faut créer une instance de
l’interface ApplicationContext
 Plusieurs implémentations existent
 Elles sont spécialisées pour certains environnements :
application Web, test unitaire, etc...
 Elles sont toutes sur le même principe : il faut charger la
configuration Spring (habituellement, charger le fichier de
configuration XML)
 La manière la plus simple :

ApplicationContext ctx = new ClassPathXmlApplicationContext( "application-context.xml");

114
Démarrage dans une application Web
 Spring est lancé via un listener dans le fichier web.xml

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF/spring/application-context.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

 Note : cette configuration lance uniquement Spring IoC,


sans Spring MVC (que nous verrons plus tard)

115
Arrêter Spring
 L’application peut parfaitement être arrêtée
«normalement» ou crasher...
 Si vous voulez explicitement arrêter Spring :
applicationContext.close();
 Les Beans sont informés qu’ils doivent s’arrêter
(@PreDestroy est appelé)
 Les Beans sont détruits
 Le context Spring n’est alors plus utilisable

116
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : Xml, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

117
Les profils
 Ils permettent de simplifier le découpage en fonction de
l’environnement
 Ils fonctionnent en configuration XML et Java (annotation
@Profile à placer sur le Bean)
 Ils simplifient encore plus la configuration d’infrastructure
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="...">
<!-- other bean definitions -->
<beans profile="dev">
<jdbc:embedded-database id="dataSource">
<jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
<jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
</jdbc:embedded-database>
</beans>
<beans profile="production">
<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>
</beans>

118
Indiquer le profil à utiliser
 Pour choisir un profil au démarrage
 Utiliser une variable d’environnement :
 -Dspring.profiles.active=production

 Le configurer au démarrage via l’API :

GenericXmlApplicationContext context = new GenericXmlApplicationContext();


context.getEnvironment ().setActiveProfiles ("prod");
context.load("classpath:META-INF/spring/applicationContext-*.xml" );
context.refresh();

119
Agenda
 L’injection de dépendance
 IoC dans Spring
 Conteneur IoC
 Configuration Objets métier Vs Objets Infrastructure
 Types de configuration : Xml, Annotations, Java config
 Les scopes des beans
 Le cycle de vie des beans
 Démarrer et arrêter Spring
 Les profils
 Les application context hiérarchiques

120
Les «application context»
hiérarchiques
 Pour l’instant, nous avons vu que Spring permettait de
créer un «application context», dans lequel étaient
configurés un certain nombre de Beans
 En fait on peut créer plusieurs «application context» et
les mettre dans une hiérarchie
 Cette notion est importante, car elle est très
fréquemment utilisée : Spring MVC et Spring Web
Services fonctionnent de cette manière

121
Fonctionnement de la hiérarchie des
«application contexts»
 Cette hiérarchie «d’application contexts» permet de donner une visibilité aux
Beans
 Les Beans enfants «voient» les Beans parents
 L’inverse n’est pas vrai

Application Context «Web»

Application Context «Métier»

 On peut injecter un Bean «métier» dans Spring MVC


 C’est ainsi qu’on appelle la couche métier depuis la couche Web
 On ne peut pas faire l’inverse
 Ce serait une erreur en termes d’architecture
 Cela permet aussi de simplifier la configuration

122
Conclusion sur Spring Core
 Nous avons vu les bases de la configuration de Spring
 La configuration, en XML, par annotation et par Java config
 L’injection de dépendances
 Le cycle de vie des Beans
 Ces concepts vont nous servir tout au long de ces 4
jours de cours
 Il est donc essentiel de bien les maîtriser
 Nous allons les revoir à plusieurs reprises

123
Exercices

124
Préparation des exercices
 Téléchargements des binaires :
 Eclipse Mars : http://www.eclipse.org/downloads/packages/eclipse-ide-java-ee-
developers/mars1
 Jdk 1.8 : http://www.oracle.com/technetwork/java/javase/downloads/jdk8-
downloads-2133151.html
 Maven 3.3.9 : https://maven.apache.org/download.cgi
 Ou Formation\binaires
 Projet code source à utiliser : Formation\Projet source
 Installation :
 Dézipper Eclipse dans un dossier : il est prêt à être utiliser
 Vérifier que la variable Globale JAVA_HOME est bien spécifiée et pointe sur le
répertoire d’installation de java
 Dezipper Maven dans un dossier
 Ajouter [répertoire d’installation de maven ]\bin dans la variable globale PATH
 mvn –v dans la ligne de commande pour vérifier l’installation
 Dans Eclipse associer au plugin maven l’installation que vous venez de faire :
Window/Preferences/Maven/Installations/Add

125
Exercice 1
 Ouvrir le projet source dans Eclipse :
 Dans Eclipse / Vue Navigateur / Import / Maven / Existing
maven project
 Constater les problèmes dont il souffre :
 Singleton mal géré
 Couplage lourd entre l’application cliente et les objets de la
couche service
 Couplage lourd entre les objets de la couche service et ceux
de la couche DAO
 Gestion des properties : classe DelProperties

126
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Créer un projet maven sous Eclipse : Formation-
Exercice2
 Dans Eclipse / Vue Navigateur click droit; new / Maven / Maven
project
 Cocher Create a simple project puis Next
 Indiquer :
 groupe id : ma.devisenligne
 Artifact id : Formation-Exercice2
 Name : Formation-Exercice2
 Copier dans ce projet le dossier src du projet source
fournit

127
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Ajouter la config du plugin de compilation de Maven et les dépendances de
Spring dans le fichier pom.xml (Formation\templates) :
<build>
<dependencies>
<plugins>
<dependency>
<plugin>
<groupId>org.springframework</groupId>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>spring-core</artifactId>
<artifactId>maven-compiler-plugin</artifactId>
<version>4.2.4.RELEASE</version>
<version>3.5.1</version>
</dependency>
<configuration>
<dependency>
<source>1.8</source>
<groupId>org.springframework</groupId>
<target>1.8</target>
<artifactId>spring-context</artifactId>
</configuration>
<version>4.2.4.RELEASE</version>
</plugin>
</dependency>
</plugins>
</dependencies>
</build>

 La résolution des dépendances marchera si la machine est connectée à


internet sinon, copier le contenu du dossier Formation\maven.repository
dans lecteur:\Users\[user]\.m2\repository
 Dans Eclipse mettre à jour le projet :
 Click droit sur le projet / Maven /Update project
 A faire à chaque fois que le fichier pom.xml est modifié

128
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Ajouter un fichier xml sous src/main/resources :
 Application-context.xml (template dans Formation\templates)
 Ajouter tous les beans de couche service dans ce fichier afin
de déléguer à Spring leur création :
 <bean id=‘’clientManager’’
class=‘’ma.devisenligne.service.ClientManager’’/>
 Créer des interfaces pour chacun de ces beans de la couche
Service
 Modifier le code de la classe client Application afin :
 D’utiliser le context Spring pour récupérer des instances des beans
de la couche service :
 ApplicationContext ctx = new ClassPathXmlApplicationContext(
"application-context.xml");
 Utiliser les interfaces créées plutôt que les implémentations
 Exécuter Application.Java

129
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Injection de dépendance :
 Ajouter les beans DAO dans le fichier application-context.xml
 Configurer les beans de la couche Service pour recevoir l’injection de
dépendance des beans de la couche DAO :
 FournisseurManager : Ajouter un constructeur qui reçoit le bean
IFournisseurDAO frnsDAO
<constructor-arg>
<ref bean="fournisseurDAO"/>
</constructor-arg>
 ClientManager : Ajouter les méthodes setter pour les attributs ITypeClientDAO et
IClientDAO
<property name="secteurDAO" ref="sectDAO"/>

 Faire de même pour le reste


 Provoquer des erreurs :
 Mettre dans l’attribut ref un nom de bean qui n’existe pas
 Mettre dans l’attribut name un nom d’attribut qui n’existe pas
 Constater le fait que Spring crée tous les beans avant même qu’on les invoque

130
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Instanciation par constructeur :
 C’est ce qu’on a fait par exemple dans le cas de ClientManager :
Ajouter dans le constructeur de ClientManager un
System.out.println(‘’En cours de Création’’)
 Dans la configuration de ClientManager ajouter l’attribut lazy-
init=‘’true’’
 Constater le changement d’ordre dans la trace
 Instanciation par méthode static de factory :
 Ajouter une nouvelle classe BasicManager contenant une seule
méthode statique createClientManagerInstance qui créé et renvoie
une instance de IClientManager
 Spécifier cette classe et cette méthode dans la configuration du bean
clientManager :
 <bean id="clientManager" class="ma.devisenligne.service.BasicManager"
factory-method="createClientManagerInstance" ..</bean>

131
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Ajouter deux propriétés dans ClientDAO :

private int defaultClientTypeId;


private String defaultClientTypeLibelle;

 Configurer le bean pour affecter des valeurs à ces deux attributs (1 pour l’id et ‘’Particulier’’ pour le libelle :
 <property name="defaultClientTypeId" value="1"/>
 <property name="defaultClientTypeLibelle" value="Particulier"/>
 Modifier le code de Application.java pour ne plus affecter un TypeClient au client à créer :
 //client.setType(type);
 Dans la méthode create de ClientDAO, gérer le cas où le client à créer n’a pas d’attribut « type » défini.
Utiliser les nouveaux attributs pour construire un TypeClient par défaut et l’affecter au client à créer :
if (client.getType() == null) {
TypeClient type = new TypeClient();
type.setLibelle(defaultClientTypeLibelle);
type.setId(defaultClientTypeId);
client.setType(type);
}

 Problèmes :
 Ces valeurs sont écrites en dur dans le fichier xml; comment faire pour les lire depuis le fichier del.properties ?
 Le fichier del.properties fournit plusieurs paramètres. L’application actuelle utilise la classe DelProperties pour
lire ces paramètres et les utiliser dans les beans de l’application. Comment avoir un accès uniforme à ces
valeurs properties ?

132
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Utiliser le PropertyPlaceholderConfigurer :
<bean id="placeholderConfig"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:del.properties" />
</bean>

 Modifier la config du bean ClientDAO pour utiliser les paramètres :


(client.type.particulier et client.type.particulier.libelle) de façon dynamique
depuis le fichier del.properties :
 <property name="defaultClientTypeId" value="${client.type.particulier}"/>
 <property name="defaultClientTypeLibelle"
value="${client.type.particulier.libelle}"/>
 Afin d’éviter d’utiliser la classe DelPropperties, on peut ajouter
des attributs (comme dans le cas de ClientDAO) et les
configurer avec les bonnes valeurs dans le fichier de config

133
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Créer un bean CallStateBean (Classe Java) :
 variable de classe (static) int stateReference
 Constructeur public qui incrémente cette variable de classe de 1
 Une méthode public doSomeStats() qui écrit (dans la trace) la valeur de
stateReference :
 System.out.println("Nombre d'instance créées est : " +
stateReference);
 Ajouter un attribut de type CallStateBean dans ClientManager et exposer une
méthode getter pour cet attribut (ClientManager et IClientManager)
 Déclarer ce bean dans le fichier application-context.xml:
 Indiquer le scope prototype
 Utiliser <aop:scoped-proxy/> (Attention au namespace aop dans le fichier xml, si
nécessaire prendre l’entête xml de la slide 246)
 Injecter ce bean dans le bean clientManager
 Dans Application.java ajouter du code :
 Récupérer CallStateBean depuis ClientManager
 Appeler plusieurs fois la méthode doSomeStats()
 Exécuter Application.java

134
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Créer des méthodes start() et close() sur le bean
DemandeManager :
 start peut écrire un message d’accueil
 close peut écrire un message d’adieu
 Utiliser ces méthodes pour configurer le bean
DemandeManager dans Spring :
 Ajouter l’attribut init-method=‘’start’’
 Ajouter l’attribut destroy-method=‘’close’’
 Exécuter Application.java

135
Exercice 2 – Utiliser Spring pour améliorer
la qualité du projet
 Splitter le fichier application-context.xml en plusieurs
parties :
 Ajouter un fichier dao-context.xml et mettre dedans toutes les
définitions des beans de la couche DAO
 Ajouter un fichier service-context.xml et mettre dedans toutes
les définitions des beans de la couche Service
 Ajouter dans le fichier application-context.xml des instructions
d’import de ces deux fichiers :
 <import resource="dao-context.xml"/>

 Exécuter Application.java

136
Exercice 3 : Utiliser les annotations pour la
configuration de l’application
 Modifier le fichier application-context.xml :
 indiquer la partie des sources que Spring doit scanner pour détecter
les beans à gérer : <context:component-scan base-package="ma.devisenligne.service" />
 Commenter l’import du fichier service-context.xml on n’en aura plus
besoin
 Ajouter à tous les beans de couche service l’annotation
@Service
 Ajouter à toutes les dépendances des beans de la couche
service l’annotation @Autowired :
 Au niveau du constructeur quand la dépendance est injecté par
constructeur
 Au niveau attribut ou setter si la dépendance est injectée par setter
 Exécuter Application.Java

137
Exercice 3 : Utiliser les annotations pour la
configuration de l’application
 Elargir la partie que Spring doit scanner pour trouver les beans à gérer :
ma.devisenligne
 Ajouter l’annotation @Repository à tous les beans de la couche DAO
 Commenter la configuration des beans dans le fichier dao-context.xml
 Garder le bean placeholderConfig
 Reporter l’injection des attributs du bean ClientDAO dans la classe du
bean en utilisant l’annotation @Value :
 @Value("${client.type.particulier}")
 private int defaultClientTypeId;
 @Value("${client.type.particulier.libelle}")
 private String defaultClientTypeLibelle;
 Reporter la configuration particulière du bean callStateBean dans la classe
du bean en utilisant l’annotation @scope

 Exécuter Application.java

138
Exercice 3 : Utiliser les annotations pour la
configuration de l’application
 Reprendre le comportement configuré sur le bean
DemandeManager :
 Init-method : Ajouter l’annotation @PostConstruct
 Destroy-method : Ajouter l’annotation @PreDestroy

139
Exercice 4 : Utiliser la configuration Java
 Créer un nouveau package config :
 Ajouter une classe AppConfig avec l’annotation @Configuration
 Ajouter dans cette classe tous les beans des couches DAO et Service :
@Bean
public IActiviteManager activiteManager() {
ActiviteManager activ = new ActiviteManager();
activ.setActiviteDAO(activDAO());
activ.setSecteurDAO(sectDAO());
return activ;
}

@Bean
public ICompteManager compteManager(IEtatCompteDAO etatCompteDAO, IClientDAO
clientDAO, IFournisseurDAO frnsDAO) {
return new CompteManager(etatCompteDAO, clientDAO, frnsDAO);
}

@Bean
public IDemandeDAO demandeDAO() {
return new DemandeDAO();
}

..

 Commenter le contenu du fichier application-context.xml

140
Exercice 4 : Utiliser la configuration Java
 Reproduire la dernière configuration du fichier application-context.xml :
 Mise en place du property place holder configurer :
 @PropertySource("classpath:del.properties")
 public class AppConfig {..
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
 Initialisation des attributs defaultClientTypeId et defaultClientTypeLibelle (Dans
AppConfig):
 @Value("${client.type.particulier}")
 private int defaultClientType;
 @Value("${client.type.particulier.libelle}")
 private String defaultClientTypeLibelle;
 Injection des dépendances par constructeur (FrnsManager) et par setter
(DemandeManager)
 Configuration de méthodes d’initialisation et de destruction sur le bean DemandeManager
:
 @Bean(initMethod = "start", destroyMethod = "close")
 Configuration du bean CallStateBean au scope prototype en mode proxy :
 @Bean
 @Scope(value="prototype", proxyMode=ScopedProxyMode.TARGET_CLASS)

141
Spring AOP

142
Introduction à l’AOP
 AOP : Aspect Oriented Programming, ou
Programmation Orientée Aspect
 L’un des deux concepts principaux de Spring (avec
l’Inversion de Contrôle)
 Permet de rajouter des comportements à des classes ou
des méthodes existantes
 Ajouter de la sécurité
 Ajouter la gestion des transactions
 Ajouter du monitoring
 Il s’agit de problématiques transverses
 Elles sont généralement techniques (infrastructure)
 Elles sont plus rarement métier

143
Spring AOP ou AspectJ
 Spring AOP utilise un mécanisme de proxy
 Ne fonctionne que sur des Beans Spring, et son utilisation est très
simple
 Est largement suffisant pour des besoins "normaux", ce qui fait que la
très grande majorité des utilisateurs de Spring utilise Spring AOP
 AspectJ est une technologie nettement plus complète et
complexe
 Repose sur une modification du bytecode des classes Java
 Permet de faire des choses nettement plus compliquées : injecter des
Beans Spring dans des classes standards, par exemple
 Est plus performant en production (mais cela a un impact mineur
dans la réalité : à comparer avec un accès base de données)
 Spring AOP utilise le même "langage" que AspectJ, ce qui fait
que l'on peut facilement migrer de Spring AOP vers AspectJ
 Nous allons donc nous concentrer sur Spring AOP

144
Fonctionnement de Spring AOP
 Un proxy «enrobe» le Bean Spring
 Il implémente la même interface, et peut ainsi le remplacer

145
Fonctionnement des proxys
 Ce sont normalement des proxys Java
 Technologie standard Java (introduite dans le JDK 1.3)
 Aujourd'hui suffisamment performants (impact mineur)
 Nécessitent l'utilisation d'une interface
 L'utilisation d'interfaces est recommandée de toute manière
(pour les tests et la souplesse de l'application)
 Si vos classes n'implémentent pas d'interface
 Spring AOP va utiliser CGLIB pour générer le proxy
 CGLIB est une librairie Open Source qui permet de générer
des classes à la volée
 Complique le débogage et les stack traces
 Aujourd'hui cette technologie est fiable, mais les proxys Java
restent à privilégier

146
Exemple : les transactions
 Les transactions sont l’un des Aspects techniques fournis
en standard par Spring :

@Service
@Transactional
public class TodosServiceImpl implements TodosService {

@PersistenceContext
private EntityManager em;

@Transactional (readOnly = true)


public Todo findTodo( final String todoId) {
return em.find(Todo.class, todoId);
}
}

147
Que se passe-t-il si on a plusieurs
Aspects sur le même Bean ?
 Exemple : une méthode est transactionnelle et sécurisée
 Spring ne génère qu'un seul proxy
 Spring va enchaîner ces Aspects
 L'ordre de ces Aspects peut être paramétré avec
l'interface org.springframework.core.Ordered ou l’annotation
@Order :
@Aspect
@Order(1)
public class AspectA {
@Before("............")
public void doit() {}
}
@Aspect
@Order(2)
public class AspectB {
@Before(".............")
public void doit() {}
}

148
Les concepts de l'AOP
 Join point : l'endroit où l'on veut qu’un aspect s'applique.
Avec Spring AOP, il s’agit toujours d’une méthode (du fait
de l'utilisation de proxy)
 Pointcut : une expression, utilisant la syntaxe AspectJ, qui
permet de sélectionner plusieurs Join points. Par exemple,
«toutes les méthodes qui se nomment find()».
 Advice : le code que l'on veut rajouter. On peut ajouter
ce code avant, après, autour de la méthode...
 Aspect : Pointcut + Advice

149
Les types d’advices
 Il est possible de définir 5 types d’advices
 Before advice : s’exécute avant le Join point. S’il lance une
Exception, le Join point ne sera pas appelé
 After returning advice : s’exécute après le Join point, si
celui-ci s’est bien exécuté (s’il n’y a pas eu d'Exception)
 After throwing advice : s’exécute si une Exception a été
lancée pendant l’exécution du Join point
 After advice (finally): s’exécute après le Join point, qu’il y ait
eu une Exception ou non
 Around advice : s’exécute autour du Join point. C’est l’advice
le plus puissant.

150
Configuration des aspects
 Plusieurs configurations sont possibles
 En XML
 En utilisant des annotations, dite méthode «@AspectJ»
 La méthode @AspectJ est à privilégier
 Plus simple à utiliser
 Permet d’avoir des Pointcut et les Advice au même endroit
 Pour pouvoir utiliser la méthode @AspectJ, ajouter dans
votre configuration Spring :
 <aop:aspectj-autoproxy/>

151
Définition d’un aspect
 Un Aspect est également un Bean Spring
 Mais il ne peut pas y avoir d’Aspect sur un autre Aspect
 On peut séparer le Pointcut de l’Advice, mais c’est plus lisible de tout mettre dans le même fichier
@Aspect
@Component
public class Monitor {
@Before("execution(* find(*))" )
public void monitorFinders() {
System. out.println("A Finder is fired!" );
}
}
@Aspect
public class SystemArchitecture {
@PointCut("execution(* find(*))" )
public void inFindMethods() {}
}

@Aspect
@Component
public class Monitor {
@Before("fr.exemple.SystemArchitecture.inFindMethods()" )
public void monitorFinders() {
System. out.println("A Finder is fired!" );
}
}

152
Le langage AspectJ
 La complexité vient en particulier de l’utilisation du
langage AspectJ
 Les bases du langage sont simples, mais il peut vite devenir très
complexe
 Il peut être difficile de sélectionner l’ensemble des méthodes
voulues
 Il faut faire attention à ne pas sélectionner d’autres méthodes
par erreur
 L’outillage, en particulier d’Eclipse (plug-in AspectJ) permet de
considérablement simplifier cette tâche

153
Introduction au langage AspectJ
 Spring AOP supporte un sous-ensemble du langage AspectJ
 Une expression Spring AOP/AspectJ est de la forme suivante :
execution(modifiers-pattern? ret-type-pattern
declaring-type-pattern? name-pattern(param-pattern)
throws-pattern?)

 modifiers-pattern : public, protected.. (à noter que Spring-Aop ne


supporte que public)
 Ret-type-pattern : Type d’objet de retour de la méthode
 declaring-type-pattern : Type de classe contenant la méthode
 name-pattern(param-pattern) : nom de la méthode et ses paramètres
 throws-pattern :
 C’est une sorte d’expression régulière ou de requête qui
permet de sélectionner les méthodes à instrumenter

154
Exemples de Pointcut
 Exécution de toutes les méthodes publiques
 execution(public * *(..))
 Exécution de tous les getters
 execution(* get*(..))
 Exécution de tous les getters qui retournent des String
 execution(* get*(..))
 Exécution de toutes les méthodes de l’interface ExampleService
 execution(* example. ExampleService .*(..))
 Exécution de toutes les méthodes du package example.test:
 execution(* example. test.*.*(..))
 Exécution de toutes les méthodes du package example.test et
de ses sous-packages
 execution(* example. test..*.*(..))

155
Exemples de Pointcut
 Toutes les méthodes annotées avec @Transactional
 @annotation (org.springframework .transaction .annotation
.Transactional )
 Toutes les méthodes du Bean nommé «testService»
 bean(testService)
 Toutes les méthodes de tous les Beans dont le nom se
termine par «Service»
 bean(*Service)

156
Les PointCuts peuvent être combinés
 On peut nommer des PointCuts afin de les réutiliser
 On peut les combiner avec les opérateurs logiques «&&»,
«||» et «!»

@Pointcut("execution(public * get*(..))" )
private void tousLesGetters() {}

@Pointcut("execution(* example.metier.*.*(..)" )
private void toutesLesMethodesMetier() {}

@Before("tousLesGetters() && toutesLesMethodesMetier()" )


private void tousLesGettersMetier() {
// Code métier...
}

157
Accès aux arguments des méthodes
 Il est intéressant d’avoir accès aux arguments des
méthodes sur lesquelles les Aspects s’appliquent
 On peut les «binder» sur l’Advice

@Before("bean(*Service) && args(account,..)" )


public void invoiceAccount(Account account) {
// Facture le compte pour tous les appels à une
méthode “métier”
}

158
Accès au retour des méthodes
 Des fois on a besoin d’accès à la valeur retournée par la
méthode :

@Aspect
public class AfterReturningExample {

@AfterReturning(
pointcut="* example.metier.*.*(..)",
returning="retVal")
public void doAccessCheck(Object retVal) {
// ...
}
}

159
Accès au Join Point
 Pour accéder au Join Point, il suffit de passer l’objet en premier paramètre
de l’Advice
 Vous avez alors accès à :
 Args : les arguments passés à la méthode
 Signature : la signature de la méthode
 Target : le Bean sur lequel s’applique l’Aspect
 This : l’objet en cours (le proxy entourant le Bean)

@Before("* example.test..*.*(..)" )
public void testMethod(JoinPoint joinPoint) {
System. out.println(joinPoint. getSignature ());
}

 Dans le cas de « Around Advice » le paramètre JointPoint est obligatoire

160
Exemples d’Aspects
 Un Advice «Before», qui s’exécute avant l’exécution des
méthodes métier
 Si cet Advice lance une Exception, le code métier ne sera pas appelé
 C’est le fonctionnement de Spring Security
@Aspect
public class BeforeExample {

@Before("execution(* example.metier.*.*(..))" )
public void controlAcces() {
// Vérification accès métier
}
}

<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService" expression="execution(* example.metier.*.*(..))"/>
<aop:before pointcut-ref="businessService" method=“controlAcces"/>
</aop:aspect>
</aop:config>

<bean id="aBean" class="fr.exemple.BeforeExample"/>

161
Exemples d’Aspects
 Un Advice «After Throwing», qui s’exécute si la méthode
a lancé une Exception
 C’est ainsi que fonctionne la couche «Repository» en Spring :
elle peut ainsi traiter automatiquement les Exceptions
provenant de la base de données (Transforme les exceptions
spécifiques à la technologie de Persistence en une exception
plus générique DataAccesException
 Il existe aussi «After returning» et «After» (finally)
@Aspect
public class AfterThrowingExample {

@AfterThrowing (
pointcut= "@annotation(org.springframework.stereotype.Repository)" , throwing= "e")
public void exceptionHandler(e) {
mailService. sendEmailAlert (e);
}
}

162
Exemples d’Aspects
 Un Aspect «Around»
 Doit avoir en premier argument un ProceedingJoinPoint, qui est
une sous-classe de JoinPoint
 Permet de se passer totalement de l’appel à la méthode sur
laquelle s’applique l’Aspect (on peut remplacer la méthode par
une autre)
@Aspect
public class AroundExample {
@Around("* example.test.*.*(..)" )
public Object profileMethods(ProceedingJoinPoint pjp) {
long start = Calendar. getInstance ().getTimeInMillis ();
Object result = pjp. proceed();
System. out.println(Calendar. getInstance ().getTimeInMillis () -
start);
return result;
}
}

163
Cas d’usage des Aspects
 Spring fournit de nombreux Aspects techniques
 Sécurité
 Transaction
 Gestion des Exceptions
 Vous pouvez faire vos propres Aspects
 Logging
 Monitoring
 Fonctionnalité métier particulière
 Mapper des Exceptions de façon générique

164
Astuce de configuration
 Les annotations facilitent considérablement l’écriture des Point Cut
 On matche sur l’annotation et non sur le package ou le nom de la méthode
 Exemple : l’annotation @Transactionnal plutôt que «toutes les méthodes qui
commencent par tx»
 Des fois les noms des méthodes sur lesquelles on veut appliquer un aspect
ne suivent pas pattern permettant de construire le Point Cut :
 On peut créer une annotation pour caractériser ces méthodes et faire dessus le
matching
 Nous avons déjà vu qu’il fallait découper les fichiers de configuration
Spring en fonction des fonctionnalités
 Vous pouvez avoir une configuration Spring lisant tous les fichiers suivants :
WEB-INF/spring/aop/*.xml
 Il vous suffit ensuite de copier/coller dans ce répertoire la configuration
des Aspects que vous voulez appliquer sur votre projet
 Par exemple : ajouter ou enlever un Aspect monitorant les méthodes
métier
 Il est ainsi possible d’ajouter ou de modifier des fonctionnalités de
l’application, sans modifier son code source
165
Tester unitairement un Aspect
 Les Aspects que nous avons vus sont des Beans Spring
normaux, avec des annotations
 Pas d’interface ou d’héritage particulier
 Ils sont donc testables unitairement comme n’importe
quelle classe Java simple
 Ces tests sont d’autant plus importants qu’un Aspect est censé
renfermer une fonctionnalité particulière que l’on veut
appliquer à de nombreux autres objets

166
Limitations de Spring AOP
 Ces limitations concernent Spring AOP, mais pas AspectJ
 Ne fonctionne qu’avec les Beans Spring
 Ne s’applique qu’aux méthodes publiques
 Ne fonctionne pas à l’intérieur d’un même Bean
 Cela est dû à l’utilisation des Proxys (on «passe» par une
interface Java)
 Le 3ème point (une méthode d’un Bean qui appelle
directement une autre méthode du même Bean) peut
être source de bugs et d’incompréhension sur les projets
Spring

167
Conclusion sur les Aspects
 C’est une technologie largement utilisée sur les projets
Spring
 Fiable
 Très puissante
 Elle permet de rajouter dynamiquement du code à des
méthodes, de manière transverse et non intrusive
 Bien comprendre son fonctionnement est essentiel
 Pour comprendre comment fonctionne Spring
 Pour pouvoir faire soi-même des Aspects si besoin

168
Exercices

169
Exercice 5 : Aspect pour tracer les appels
des méthodes de la couche service
 Pour réaliser cet exercice nous avons besoins de :
 Jars aspectJ et aspectjweaver;
 Projet Formation-Exercice5 qui est dans le dossier fournit Addendum1
 Importer le projet Formation-Exercice5 dans Eclipse :
 Dans la vue Navigator d’Eclipse : click droit / import / Maven / Existing Maven
Projects
 Ajouter les dépendances aspectJ et aspectjweaver sous <dependencies>:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>

 Si pas de connexion internet récupérer le contenu du dossier


addendum1/maven.repository et le mettre dans
lecteur:\Users\[user]\.m2\repository

170
Exercice 5 : Aspect pour tracer les appels
des méthodes de la couche service
 L’objectif de l’exercice est de créer un Aspect qui permet
d’écrire une trace avant et après l’exécution des méthodes des
beans de la couche service (package ma.devisenligne.service)
 Ajouter un nouveau dossier ou package « aop » sous
ma.devisenligne
 Créer une classe TraceAdvice contenant deux méthodes
afficherTraceAvantExecution et afficherTraceApresExecution
 Ces deux méthodes vont tracer l’exécution des méthodes des beans
de la couche service. Elles ont donc besoin d’accéder aux
informations sur cette méthode : son nom, le nom de son bean et
ses paramètres..
 Pour accéder à ces informations, il faut que ces méthodes prennent
en paramètre un objet de JointPoint qui représente la méthode
advisée et renvoie toutes les informations nécessaires sur elles.

171
Exercice 5 : Aspect pour tracer les appels
des méthodes de la couche service
 Le code de la méthode afficherTraceAvantExecution
public void afficherTraceAvantExecution(final JoinPoint joinpoint)
throws Throwable {
String nomMethode = joinpoint.getTarget().getClass().getSimpleName() + "."
+ joinpoint.getSignature().getName();

final Object[] args = joinpoint.getArgs();


final StringBuffer sb = new StringBuffer();
sb.append("Debut methode : ");
sb.append(nomMethode);
sb.append(" avec les parametres : (");

for (int i = 0; i < args.length; i++) {


sb.append(args[i]);
if (i < args.length - 1) {
sb.append(", ");
}
}
sb.append(")");
System.out.println(sb);
}

 Ajouter la méthode afficherTraceApresExecution de la même façon


 Configurer TraceAdvice comme un bean géré par Spring :
 @Component("traceAdvice")

172
Exercice 5 : Aspect pour tracer les appels
des méthodes de la couche service
 Dans la suite, configurer :
 Créer un fichier aop-context.xml pour contenir la config de notre aspect (ne
pas oublier l’entête xml)
 Un aspect sur le bean TraceAdvice
 Un pointcut pour matcher toutes les méthodes des beans de la couche service
 La méthode afficherTraceAvantExecution : comme advice before
 La méthode afficherTraceApresExecution : comme advice after
<aop:config>
<aop:aspect id="traceAspect" ref="traceAdvice" order="1">
<aop:pointcut expression="execution(* ma.devisenligne.service.*.*(..))" id="serviceMethode"/>
<aop:before method="afficherTraceAvantExecution" pointcut-ref="serviceMethode"/>
<aop:after method="afficherTraceApresExecution" pointcut-ref="serviceMethode"/>
</aop:aspect>
</aop:config>

 L’attribut ref de la balise <aop:aspect correspond à l’id du bean TraceAdvice


 Importer le fichier aop-context.xml dans application-context.xml
 Exécuter Application.java et constater les traces mise en place avant et
après chaque invocation d’une méthode d’un bean de service

173
Exercice 5 : Aspect pour tracer le temps
d’execution d’une méthode
 Créer une nouvelle classe MonitoringAdvice dans le package ma.devisenligne.aop :
 Ajouter dans cette classe la méthode monitorMethodCall(ProceedingJoinPoint proceedingJoinPoint)
 Cette méthode servira à notre aspect Around, donc elle doit prendre en paramètre
ProceedingJoinPoint afin de pouvoir lancer la méthode advisée :
public void monitorMethodCall(ProceedingJoinPoint proceedingJoinPoint)
throws Throwable {
String nomMethode = proceedingJoinPoint.getTarget().getClass().getSimpleName() + "."
+ proceedingJoinPoint.getSignature().getName();
final Object[] args = proceedingJoinPoint.getArgs();
final StringBuffer sb = new StringBuffer();
sb.append("Aspect Around L'execution de la méthode : ");
sb.append(nomMethode);
sb.append(" avec les parametres : (");
for (int i = 0; i < args.length; i++) {
sb.append(args[i]);
if (i < args.length - 1) {
sb.append(", ");
}
}
sb.append(")");
long start = Calendar.getInstance ().getTimeInMillis ();
proceedingJoinPoint.proceed();
long duration = Calendar.getInstance ().getTimeInMillis () - start;
sb.append(" a duré : ")
.append(duration)
.append(" millisecondes");
System.out.println(sb);
}

174
Exercice 5 : Aspect pour tracer le temps
d’execution d’une méthode
 Pour la définition du PointCut : Utiliser une annotation
spécifique pour caractériser les pointcut qu’on veut
associer à l’aspect de monitoring :
 Ajouter un dossier « annotation » sous ma.devisenligne
 Ajouter une nouvelle annotation Monitored dans ce package :
@Target( {ElementType.TYPE, ElementType.METHOD})
@Retention( RetentionPolicy.RUNTIME )
public @interface Monitored {

 C’est une annotation sans élément qui servira juste à


indiquer les méthodes sur lesquelles on veut appliquer
l’aspect

175
Exercice 5 : Aspect pour tracer le temps
d’execution d’une méthode
 Configurer Spring pour prendre en compte les annotations pour la définition des aspects :
 Ajouter la balise <aop:aspectj-autoproxy/> dans le fichier aop-context.xml
 Configurer le bean MonitoringAdvice :
 Comme un bean à gérer par Spring : @Component("monitoringAdvice")
 Comme un aspect : @Aspect
 D’ordre d’exécution 2 : @Order(2)
 Configurer un Pointcut dans Monitoring Advice :
 @Pointcut("@annotation(ma.devisenligne.annotation.Monitored)")
 public void monitoredMethod() {}
 Configurer la méthode monitorMethodCall dans MonitoringAdvice comme Advice de type
Around :
 @Around("monitoredMethod()")
 Exécuter Application.java
 Constater la trace
 Si la trace ne contient pas les éléments attendus c’est normal, nous n’avons appliquer
l’annotation @Monitored (qui sert de PointCut à l’aspect) à aucune méthode de service
 Ajouter l’annotation @Monitored à la méthode addClient de ClientManager
 Exécuter Application.java
 Constater l’ordre de l’execution des deux aspects mis en place
 Changer les valeurs des configs order dans les deux aspects et constater le changement

176
Accès aux données – Spring
Jdbc

177
Une abstraction ?
 Nous allons voir la 3ème (et dernière) grande
fonctionnalité de Spring
 Les deux premières : Inversion de Contrôle et Programmation
Orientée Aspect
 La 3ème : la mise à disposition d’abstractions, qui simplifient
des problématiques techniques courantes
 Ces abstractions ne peuvent fonctionner que grâce à
l’AOP, qui lui-même est permis grâce à l’IoC

178
Introduction à Spring JDBC
 Spring JDBC est la première abstraction que nous allons
étudier
 C’est l’une des plus simples
 Nous allons vite voir qu’elles fonctionnent toutes de la même
manière, et qu’une fois le principe compris il est très aisé d’en
apprendre une nouvelle
 Spring JDBC va nous aider à faire des requêtes en base de
données
 Gestion des accès à la base de données
 Aide à l’exécution du SQL
 Gestion des Exceptions

179
Spring JDBC : pour quoi faire ?
 Spring JDBC simplifie l’utilisation de JDBC
 Mais il est nettement plus basique qu’Hibernate
 Il n’est donc à utiliser que dans des cas assez particuliers
 Un projet très simple sur lequel on ne veut pas utiliser
Hibernate
 Un point précis d’un projet, pour lequel on ne veut pas utiliser
Hibernate afin d’être plus proche de la base de données

180
Spring JDBC : pour quoi faire ?

181
Configuration de JdbcTemplate
 JdbcTemplate est la classe principale de Spring JDBC
 Elle est thread-safe
 Elle est parfois configurée en tant que Bean Spring
 Voici la configuration la plus classique
 @Repository hérite de @Component : cette annotation marque
un Bean Spring de la couche d’accès aux données
@Repository
public class JdbcExampleDao implements ExampleDao {
private JdbcTemplate jdbcTemplate;

@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
}
182
Configuration de la data source
 La data source utilisée est un pool de connexion à la base
de données
 Voici une configuration Spring typique :

<context:component-scan base-package= "example.test" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"


destroy-method= "close">
<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>

<context:property-placeholder location="jdbc.properties" />

183
Gestion de la Data Source
 La Data Source est gérée par Spring
 C’est Spring JDBC qui va gérer le fait d’ouvrir une connexion
JDBC et de la refermer
 Il n’y a plus à coder ces parties techniques
 Cela élimine les risques d’erreurs : en cas d’Exception, c’est Spring
qui se charge de correctement fermer la connexion
 Spring va encapsuler la création d’objet comme les
PreparedStatement et les appels aux méthodes jdbc classiques
 Cela reprend ce que nous avons vu au début
 Spring se charge de la plomberie : ouvrir/fermer les connexions,
gérer les Exceptions
 Au développeur de coder le métier : la requête SQL

184
Utilisation de JdbcTemplate
 JdbcTemplate permet de faire des requêtes JDBC en une seule ligne
 Beaucoup plus pratique que la méthode «manuelle»
String sql = "SELECT * FROM EMPLOYEE WHERE ID = ?";
Connection conn = null;
try {
int nbAccounts = jdbcTemplate. queryForInt ("select conn = dataSource.getConnection();
count(*) from account" ); PreparedStatement ps = conn.prepareStatement(sql);
ps.setInt(1, id);
int nbGoodCustomers = jdbcTemplate. queryForInt ( Employee employee = null;
"select count(*) from account where balance > ResultSet rs = ps.executeQuery();
?" , 10000); if (rs.next()) {
employee = new Employee(
rs.getInt("ID"),
rs.getString("NAME"),
rs.getInt("AGE")
);
}
rs.close();
ps.close();
return employee;
} catch(SQLException e) {
throw new RuntimeException(e);
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
}
}
}
185
Utilisation de JdbcTemplate
 JdbcTemplate gère également les create/update/delete
 On peut donc faire un CRUD assez simplement

jdbcTemplate.update(
"insert into account (user_id, creation_date, balance) " +
"values (?, ?, ?)" ,
account. getUserId(),
account. getCreationDate (),
user. getBalance ());

jdbcTemplate.update(
"update account set balance=? where id=?" ,
account. getBalance (),
account. getId());

186
Utilisation de JdbcTemplate
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");

public List<Map<String, Object>> getList() {


return this.jdbcTemplate.queryForList("select * from mytable");
}

 Renvoie une structure de la forme [{name=Bob, id=1}, {name=Mary, id=2}]

public int[] batchUpdate(final List<Actor> actors) {


int[] updateCounts = jdbcTemplate.batchUpdate("update t_actor set first_name = ?, "
+ "last_name = ? where id = ?",
new BatchPreparedStatementSetter() {
public void setValues(PreparedStatement ps, int i) throws SQLException {
ps.setString(1, actors.get(i).getFirstName());
ps.setString(2, actors.get(i).getLastName());
ps.setLong(3, actors.get(i).getId().longValue());
}
public int getBatchSize() { return actors.size(); }
});
return updateCounts;
}

187
Utilisation d’un RowMapper
 Un RowMapper permet de mapper un objet Java avec une
ligne retournée par une requête SQL
 Cela permet de faire un mapping objet/relationnel très simple

jdbcTemplate.query( "select id, user_id, balance from account" ,


new ParameterizedRowMapper<Account>() {
public Account mapRow(ResultSet rs, int i) throws SQLException {
return new Account(rs. getLong("id"),
rs. getInt("user_id"),
rs. getInt("balance"));
}
}
);

188
Utilisation des Callbacks
 Le RowMapper que nous venons de voir est ce que l’on
appelle un Callback
 Il s’agit d’une classe anonyme, implémentant une interface
fournie par Spring
 Cela permet de recevoir dans notre code métier un appel
provenant de Spring
 Dans ce cas, Spring nous donne le ResultSet et le numéro de la
ligne
 Ce mécanisme de Templates et de Callbacks est très
largement utilisé dans les différentes abstractions fournies
par Spring

189
Gestion des Exceptions
 Le JdbcTemplate ne lance pas de SQLException
 Relancer ces Exceptions jusqu’à la couche de présentation ne
sert à rien
 Cela casse l’architecture logicielle : si une SQLException est
remontée, on sait que dans les couches basses on utilise JDBC
 Spring JDBC enrobe les SQLException par des
DataAccessException
 Ce sont des exceptions de type Runtime (il n’y a pas à les
catcher)
 Elles sont communes à toutes les technologies d’accès aux
données (=utilisé par Hibernate et JPA également)
 Elles permettent toujours d’avoir accès à l’Exception d’origine
(pas de perte de connaissance)

190
Conclusion sur Spring JDBC
 Abstraction relativement simple au-dessus de JDBC
 Gestion automatique de la DataSource
 Gestion automatique des Exceptions
 Classes utilitaires simples, utilisant des Templates et des
Callbacks
 Spring JDBC est pratique pour
 Faire des CRUD simples
 Avoir un accès «bas niveau» à la base de données
 Spring JDBC peut être utilisé conjointement à
Hibernate/JPA, qui fournit une solution bien plus
complète d’accès aux bases de données relationnelles

191
Exercices

192
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Afin de réaliser cet exercice nous aurons besoin de:
 Moteur de base de données HSQLDB hsqldb-2.3.3 :
 Dossier fourni : Addendum2\Binaries
 Script de création du schéma de la BD à utiliser :
 Dossier fourni :Addendum2\scripts bd
 Projet Formation-Exercice6 qui est dans le dossier fournit Addendum2
 Configuration de HSQLDB :
 Dézipper le fichier hsqldb-2.3.3
 Config d’une base de données : Dans le sous dossier hsqldb de hsqldb-2.3.3 , ajouter un
fichier server.properties contenant les lignes suivantes :
 server.database.0=file:hsqldb/DevisEnLigneDb
 server.dbname.0=devisenligne
 Dans la ligne de commande windows aller dans le dossier hsqldb-2.3.3\hsqldb
 Exécuter java -classpath lib/hsqldb.jar org.hsqldb.server.Server
 Constater la création d’un nouveau dossier hsqldb sous hsqldb-2.3.3\hsqldb contenant les fichiers
de la base de données devisenligne
 Le serveur HSQLDB est maintenant lancé :
 Arrêter le serveur HSQLDB : taper control+C dans la ligne de commande
 Relancer le serveur avec la commande : java -classpath lib/hsqldb.jar org.hsqldb.server.Server --
database.0 file:hsqldb/DevisEnLigneDb --dbname.0 devisenligne

193
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Création des tables de la base de données :
 Exécuter le fichier hsqldb-
2.3.3\hsqldb\bin\runManagerSwing.bat pour lancer un utilitaire
graphique de gestion de la base de données configurée dans l’étape
précédente
 Saisir les informations suivante dans la fenêtre « Connect »
 Dans Setting Name indiquer un nom pour retrouver ces informations
lors de connexions ultérieures : Conn_Devisenligne par exemple
 Dans Type : choisir HSQL Database Engine In-Memory
 Dans Driver : org.hsqldb.jdbcDriver (driver jdbc de HSQLDB)
 Dans URL : jdbc:hsqldb:hsql://localhost/devisenligne (url jdbc
d’accès à la base de données qu’on vient de créer dans l’étape
précédente)
 Dans User : SA (utilisateur par défaut embarqué dans le moteur, il n’a
pas de mot de passe associé)
 Dans Password : laisser vide
 HSQL Database Manager est maintenant lancé

194
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Création des tables de la base de données :
 Dans HSQL Database Manager aller dans File/Open Script
 Indiquer le fichier Addendum2\scripts bd\crebas.sql
 Click sur « Execute SQL »
 Constater l’apparition de dossier nommé
PUBLIC.NOM_TABLE pour toutes les tables du script
 Ensuite de la même façon exécuter, dans l’ordre, les scripts
constraints.sql et insertSecteurActivite.sql (qui sont dans
Addendum2\scripts bd)
 La base de données est créée et prête à être utilisée
 Le HSQL Database Manager nous permettra par la suite
de lancer les requêtes sql de vérification des différentes
étapes de l’exercice.

195
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Importer le projet Formation-Exercice6 dans Eclipse :
 Dans la vue Navigator d’Eclipse : click droit / import / Maven / Existing Maven Projects
 Ajouter les dépendances spring-jdbc, spring-tx et hsqldb sous <dependencies>: dans le
fichier pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
 Si pas de connexion internet récupérer le contenu du dossier addendum1/maven.repository et le mettre
dans lecteur:\Users\[user]\.m2\repository
 Ne pas oublier de mettre à jour le projet dans Eclipse après chaque modification du pom.xml

196
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Configuration de l’accès à la base de données :
 Dans resources/del.properties ajouter les informations de connexion
(identiques à ceux utilisée dans HSQL DataBase Manager) :
## datasource informations
jdbc.driverClassName=org.hsqldb.jdbc.JDBCDriver
jdbc.url=jdbc:hsqldb:hsql://localhost/devisenligne
jdbc.username=SA
jdbc.password=

 Dans dao-context.xml ajouter la configuration d’un bean datasource :


<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>

 Notez l’utilisation d’une implémentation de Datasource fournit par


Spring DriverManagerDataSource. Dans notre cas c’est une
implémentation basique qui ne fait pas de pooling de connection

197
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Ajouter dans la couche DAO, l’accès à la façade JdbcTemplate de Spring :
 Dans ClientDAO ajouter :
 Un attribut privé de type JdbcTemplate
 Injecter le bean dataSource configuré dans l’étape précédente :
@Autowired
public void setDatasource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
 Modifier l’implémentation de la méthode create(Client) pour persister le client à créer
dans la base de données :
 Utiliser la méthode update de JdbcTemplate pour insérer le client dans la base de données
 Construire une requête sql d’insertion avec paramètre
 Passer les paramètres dans l’ordre
this.jdbcTemplate.update(
"insert into client (TYPECLIENT_ID, ETATCOMPTE_ID, CLIENT_PWD, CLIENT_LOGIN, CLIENT_ADRESSE, CLIENT_FAX,
CLIENT_TELEPHONE, CLIENT_EMAIL, CLIENT_PRENOM, "
+ "CLIENT_NOM, CLIENT_RAISSOC, CLIENT_TELPORTABLE) "
+ "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
client.getType().getId(), client.getEtat().getId(), client.getPwd(), client.getLogin(),
client.getAdresse(), client.getFax(), client.getTel(),
client.getEmail(), client.getPrenom(), client.getNom(), client.getRaisonSociale(),
client.getTelPortable())

198
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Regarder le code de la classe ClientApplication.java
 Exécuter la classe ClientApplication
 Vérifier que les clients ont été créés dans la base de données : exécuter la requête select * from client (HSQL DB Manager)
 Implémenter la méthode findClientById(id) de ClientDAO
 Utiliser la méthode queryForObject de jdbcTemplate :
Client client = this.jdbcTemplate.queryForObject(
"select CLIENT_ID, TYPECLIENT_ID, ETATCOMPTE_ID, CLIENT_PWD, CLIENT_LOGIN, CLIENT_ADRESSE,
CLIENT_FAX, CLIENT_TELEPHONE, CLIENT_EMAIL, CLIENT_PRENOM,
+ "CLIENT_NOM, CLIENT_RAISSOC, CLIENT_TELPORTABLE from client where client_id = ?",
new Object[]{id},
new RowMapper<Client>() {
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = new Client();
client.setPrenom(rs.getString("CLIENT_PRENOM"));
client.setNom(rs.getString("CLIENT_NOM"));
client.setAdresse(rs.getString("CLIENT_ADRESSE"));
client.setEmail(rs.getString("CLIENT_EMAIL"));
client.setFax(rs.getString("CLIENT_FAX"));
client.setId(rs.getInt("CLIENT_ID"));
client.setLogin(rs.getString("CLIENT_LOGIN"));
client.setPwd(rs.getString("CLIENT_PWD"));
client.setRaisonSociale(rs.getString("CLIENT_RAISSOC"));
client.setTel(rs.getString("CLIENT_TELEPHONE"));
client.setTelPortable(rs.getString("CLIENT_TELPORTABLE"));
EtatCompte etat = new EtatCompte();
etat.setId(rs.getInt("ETATCOMPTE_ID"));
client.setEtat(etat);
TypeClient typeClient = new TypeClient();
typeClient.setId(rs.getInt("TYPECLIENT_ID"));
client.setType(typeClient);
return client;
}
199 });
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Exécuter la classe ClientApplication
 Vérifier que le client avec l’id 1 a été bien récupéré.
 Avez-vous constaté qu’il manque les informations EtatCompte et
TypeClient ?
 Dans ClientManager la méthode getClient tente de les récupérer en
appelant les DAOs concernés mais comme les méthodes ne sont
pas implémentées les données renvoyées sont nulles
 Implémenter les méthodes findById de EtatCompteDAO et
getTypeClientById de TypeClientDAO :
 Tout comme la méthode findClientById(id) de ClientDAO
 Trouver les noms des colonnes des tables ETAT_COMPTE et
TYPE_CLIENT dans HSQL DataBase Manager
 Exécuter la classe ClientApplication et constater que les
informations EtatCompte et TypeClient sont présentes cette fois

200
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Implémenter la méthode findClients de ClientDAO en utilisant la méthode query
de JdbcTemplate :
List<Client> clients = this.jdbcTemplate.query(
"select CLIENT_ID, TYPECLIENT_ID, ETATCOMPTE_ID, CLIENT_PWD, CLIENT_LOGIN,
CLIENT_ADRESSE, CLIENT_FAX, CLIENT_TELEPHONE, CLIENT_EMAIL, CLIENT_PRENOM, "
+ "CLIENT_NOM, CLIENT_RAISSOC, CLIENT_TELPORTABLE from client",
new RowMapper<Client>() {
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = new Client();
client.setPrenom(rs.getString("CLIENT_PRENOM"));
client.setNom(rs.getString("CLIENT_NOM"));
client.setAdresse(rs.getString("CLIENT_ADRESSE"));
client.setEmail(rs.getString("CLIENT_EMAIL"));
client.setFax(rs.getString("CLIENT_FAX"));
client.setId(rs.getInt("CLIENT_ID"));
client.setLogin(rs.getString("CLIENT_LOGIN"));
..

 Constat : le code de construction des objets clients dans la classe anonyme est
identique à celui utilisé dans la méthode findClientById :
  Refactoring :
 Créer une classe ClientMapper qui impémente RowMapper<Client>,
 déplacer dedans le code de la classe anonyme
 Utiliser la nouvelle classe dans les appels de findClients et findClientById
 Voir l’implémentation dans la slide suivante

201
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
private static final class ClientMapper implements RowMapper<Client> {
public Client mapRow(ResultSet rs, int rowNum) throws SQLException {
Client client = new Client();
client.setPrenom(rs.getString("CLIENT_PRENOM")); Inner class static dans
client.setNom(rs.getString("CLIENT_NOM")); ClientDAO
client.setAdresse(rs.getString("CLIENT_ADRESSE"));
client.setEmail(rs.getString("CLIENT_EMAIL"));
client.setFax(rs.getString("CLIENT_FAX"));
client.setId(rs.getInt("CLIENT_ID"));
Utilisation de la nouvelle
client.setLogin(rs.getString("CLIENT_LOGIN"));
client.setPwd(rs.getString("CLIENT_PWD")); Classe ClientMapper dans la
client.setRaisonSociale(rs.getString("CLIENT_RAISSOC")); méthode findClientById
client.setTel(rs.getString("CLIENT_TELEPHONE"));
client.setTelPortable(rs.getString("CLIENT_TELPORTABLE"));
EtatCompte etat = new EtatCompte();
etat.setId(rs.getInt("ETATCOMPTE_ID")); Utilisation de la nouvelle
client.setEtat(etat); Classe ClientMapper dans la
TypeClient typeClient = new TypeClient();
méthode findClients
typeClient.setId(rs.getInt("TYPECLIENT_ID"));
client.setType(typeClient);
return client;
}
}

Client client = this.jdbcTemplate.queryForObject(


"select CLIENT_ID, TYPECLIENT_ID, ETATCOMPTE_ID, CLIENT_PWD, CLIENT_LOGIN, CLIENT_ADRESSE, CLIENT_FAX,
CLIENT_TELEPHONE, CLIENT_EMAIL, CLIENT_PRENOM, "
+ "CLIENT_NOM, CLIENT_RAISSOC, CLIENT_TELPORTABLE from client where client_id = ?",
new Object[]{id},new ClientMapper());

List<Client> clients = this.jdbcTemplate.query(


"select CLIENT_ID, TYPECLIENT_ID, ETATCOMPTE_ID, CLIENT_PWD, CLIENT_LOGIN, CLIENT_ADRESSE, CLIENT_FAX,
CLIENT_TELEPHONE, CLIENT_EMAIL, CLIENT_PRENOM, "
+ "CLIENT_NOM, CLIENT_RAISSOC, CLIENT_TELPORTABLE from client",
202 new ClientMapper());
Exercice 6 : Utilisation de Spring Jdbc pour
l’accès à une base de données
 Exécuter ClientApplication, constater la récupération de la liste des clients
dans la base de données

 Provoquer une erreur dans la couche Jdbc :


 Dans ClientApplication ajouter le code suivant juste avant l’invocation de la
création du premier client :
 TypeClient typeClient = new TypeClient();
 typeClient.setId(209);
 client.setType(typeClient);
 Comme aucun type client avec l’id 209 n’existe dans la base de données et qu’il
existe une contrainte référentiel de Client sur TYPE_CLIENT Une exception
sera levée
 Constater que l’exception levée est propre à Spring ce n’est pas celle de
Jdbc, regarder le détail de la trace pour voir la transformation de
l’exception opérée par Spring
 Rappel : c’est l’annotation @Repository qui indique à Spring que nous
voulons que les exceptions jdbc soient encapsulées par celles de Spring.

203
Accès aux données – Spring
Transactions

204
Introduction aux transactions
 Les transactions sont typiquement gérées par une base
de données relationnelles
 Une transaction est normalement ACID
 Atomique
 Cohérente (Consistant)
 Isolée
 Durable
 En Java, nous pouvons les gérer via des APIs simples, par
exemple en JDBC ou avec JTA

205
Utilité des transactions
 Il est primordial de gérer les transactions si on veut avoir
des données de qualité
 Les transactions permettent de traiter un ensemble
d’opérations comme une seule opération
 Pas de données incohérentes dans la base
 Les transactions permettent également d’avoir de
meilleures performances
 Il vaut mieux faire 10 requêtes dans une transaction que de
faire 10 requêtes dans 10 transactions
 C’est d’ailleurs une des raisons de leur utilisation dans les
batchs

206
Exemple d’une transaction
 3 requêtes : 1 lecture, 2 écritures
 1 seule transaction (matérialisée par la flèche)
 Soit les 2 modifications sont appliquées, soit aucune n’est
appliquée

207
Les transactions en Java
 En JDBC
conn = dataSource. getConnection ();
conn.setAutoCommit (false);
// requêtes SQL
conn.commit();

 Avec JTA (Java Transaction API)


UserTransaction utx = ctx. getUserTransaction ();
// Démarrage de la Transaction
utx.begin();
// Exécution des requêtes
utx.commit();

208
Spring Transactions
 Spring propose une couche d’abstraction
 Gère les transactions JDBC, Hibernate, JTA etc... de manière
homogène
 Permet de simplement configurer ses transactions : utilisation
d’annotations ou d’XML, sans utilisation obligatoire de code
 Cela permet d’avoir une meilleure architecture
 Les transactions sont déclarées dans la couche métier
(service), et non dans la couche d’accès aux données
(repository / DAO)
 Les transactions ne dépendent pas d’une technologie
particulière d’accès aux données (JDBC)

209
Utilisation simple avec Spring
 Configurer un gestionnaire de transaction
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.
DataSourceTransactionManager" >
<property name="dataSource" ref="dataSource" />
</bean>

 Dire à Spring que l’on veut utiliser les annotations


<tx:annotation-driven/>

 Utiliser les annotations


@Transactional
public void uneMethodeMetier() {
// Unité de travail atomique
}

210
Fonctionnement dans Spring
 Spring fournit un Aspect spécialisé
 Le Point Cut est sur les méthodes annotées @Transactional
 L’Advice est de type Around, et ajoute la gestion des
transactions autour des méthodes annotées

 C’est le fonctionnement que nous avons vu dans le


chapitre sur Spring AOP, avec la génération d’un proxy
 Ne fonctionne que sur les Beans Spring
 Ne fonctionne que sur les méthodes publiques
 Ne fonctionne pas à l’intérieur d’un même Bean

211
Configuration d’un gestionnaire de
transaction
 Le gestionnaire de transaction est une classe fournie par
Spring
 Il fait partie de l’infrastructure
 Il est spécifique à la technologie utilisée
 Hors JTA, il a besoin d’une Data Source pour être configuré
 Par convention, il possède l’id «transactionManager»
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

 Si vous êtes dans un serveur d’applications (Websphere,


Weblogic...), Spring peut retrouver automatiquement le
gestionnaire de transactions de ce serveur (utilisant l’API
JTA) : <tx:jta-transaction-manager/>

212
Utilisation des annotations
 L’annotation @Transactional peut être mise sur une classe
(toutes les méthodes publiques sont transactionnelles) ou
sur une méthode
 Cette annotation a un certain nombre de paramètres :
Isolation, Propagation, Timeout, readOnly…
@Service
@Transactional
public class TodoListsServiceImpl implements TodoListsService {

@Transactional (readOnly = true)


public TodoList findTodoList(String listId) {
// Code métier, utilisant Hibernate par exemple
}
}

213
Variante : utilisation du XML
 On peut également utiliser une configuration XML
 On code alors un Point Cut spécifique : par exemple toutes les
méthodes des classes d’un package spécifique
 On privilégiera la configuration par annotations : elle est plus
simple et plus lisible
<aop:config>
<aop:pointcut id="serviceBeans"
expression= "execution(public * test.service.*(..))" />
<aop:advisor pointcut-ref= "serviceBeans" advice-ref= "txAdvice" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="find*" read-only= "true"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>

214
Le TransactionTemplate
 Si la configuration par annotations ou XML n’est pas suffisante, Spring propose une API
 Elle utilise le système des Templates et des Callbacks que nous avons déjà vus pour Spring JDBC

public class SimpleService implements Service {


// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;

// use constructor-injection to supply the PlatformTransactionManager


public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}

public Object someServiceMethod() {


return transactionTemplate.execute(new TransactionCallback() {
// the code in this method executes in a transactional context
public Object doInTransaction(TransactionStatus status) {
try {
updateOperation1();
return resultOfUpdateOperation2();
catch(Exception e) {
status.setRollbackOnly();
return null;
}
}
});
}
}

215
Exemple
 On peut configurer la transaction via l’annotation
@Transactional : si elle est «read-only», spécifier un
timeout ou un mode de propagation particulier

@Transactional (readOnly = true, timeout = 30,


propagation = Propagation. REQUIRES_NEW )
public void maMethodeMetier() {
//
}

216
Les transactions XA
 Les transactions XA permettent d’avoir une seule transaction en
utilisant des ressources différentes
 Deux bases de données
 Une base de données et un serveur JMS
 Pour fonctionner, il faut que ces ressources et le gestionnaire de
transaction supportent les transactions XA
 WebSphere, Weblogic proposent des gestionnaires de transaction XA
 Avec Spring, vous pouvez configurer un gestionnaire de transaction XA
externe : Atomikos, Bitronix
 Avec Spring, c’est juste une configuration différente de
l’infrastructure
 Aucun changement dans le code !
 Nous vous déconseillons d’utiliser cette technologie
 On peut généralement obtenir le même résultat de manière plus simple
(sans faire de transaction distribuée)
 De par son fonctionnement, elle est peu performante

217
Conclusion sur les transactions
 L’utilisation des transactions est essentielle, elle vous
garantie la qualité des données et une bonne
performance
 La configuration des transactions avec Spring est très
simple
 Utilisation des annotations
 Une configuration avancée reste un simple paramétrage
 En termes d’architecture, cela permet de gérer les transactions
au niveau de la couche métier (service), et plus dans les
couches basses (repository/DAO)

218
Exercices

219
Exercice 6 (suite) : Ajout du support
transactionnel aux méthodes de service
 Inspecter le code de la classe TransactClientApplication :
 Cette classe crée deux objets Client qu’elle empile dans une liste
 Avant d’appeler la méthode de service clientManager.addClients
 Elle lance une création groupée de plusieurs clients
 Les clients doivent créés ensemble, si l’un des clients n’est pas
persisté pour un quelconque problème l’autre ne doit pas être
persisté..
 Lancer TransactClientApplication
 Le premier client est persisté alors que le deuxième ne l’est pas en
raison d’une erreur d’intégrité référentielle
 Configurer le support des transactions dans Spring afin
d’éviter ce problème

220
Exercice 6 (suite) : Ajout du support
transactionnel aux méthodes de service
 Configurer le support des transactions dans Spring :
 Ajouter la définition du bean qui va gérer les transactions
 Activer le support des annotations pour la déclaration des
transactions
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="txManager"/>

 Dans ClientManager ajouter l’annotation Transaction sur la


méthode addClients : @Transactional
 Exécuter TransactClientApplication :
 Constater qu’aucun des deux clients n’est créé cette fois

221
Accès aux données – Spring
Hibernate JPA

222
Introduction à l’ORM
 ORM == Object-Relational Mapping
 Cette technologie permet de mapper automatiquement
des objets sur des tables
 Cela facilite le développement
 Cela donne une architecture logicielle de meilleure qualité
 Il existe de nombreuses solutions d’ORM
 JPA n’est qu’une API
 EclipseLink en est l’implémentation officielle
 Hibernate en est l’implémentation (de loin) la plus populaire

223
Qu’apporte une solution d’ORM ?
 Une solution d’ORM permet de se concentrer sur des
objets Java (souvent appelés «objets de domaine», pour le
domaine métier)
 Le code SQL pour créer/sélectionner/mettre à jour/effacer ces
données est automatiquement généré
 Il n’y a plus de dépendance avec une base de données
spécifique (différences dans le langage SQL)
 Elle fournit généralement des mécanismes avancés de cache et
de chargement des données qui font qu’elle est au final plus
performante que du SQL codé «à la main»

224
Les problèmes soulevés par l’ORM
 Une technologie de mapping est nécessaire car il y a des
différences fondamentales entre une table et un objet
 Il n’y a pas d’héritage ou de polymorphisme en base de
données
 La gestion des associations est différente (one-to-many et
many-to-many)
 De nombreux types de données sont différents (par exemple il
y a de nombreuses façons de stocker une String en base de
données)
 Les contraintes de validation ne sont pas gérées de la même
manière...
 Une solution d’ORM a pour but d’alléger ou de simplifier
ces problèmes

225
Quelle implémentation choisir ?
 Hibernate est de très loin l’implémentation la plus
répandue
 Hibernate a largement fait ses preuves en termes de qualité et
de performance
 Il faut avoir une très bonne raison pour ne pas prendre
Hibernate
 Privilégier l’API JPA
 C’est ce que recommande également l’équipe Hibernate !
 Lorsque JPA n’est pas suffisant, on peut toujours compléter
avec l’API spécifique Hibernate

226
Mapping d’une entité simple (1/2)

@Entity
@Table(name = "t_todo")
public class Todo implements Serializable {
@Id
@Column(name = "id")
private String todoId;

@Column(name = "creation_date" )
private Date creationDate;

private String description;

private int priority;


// getters et setters
}

227
Mapping d’une entité simple (2/2)
 Ce mapping utilise uniquement des annotations JPA
 Les annotations sont :
 Au niveau de la classe, pour la mapper sur une table donnée
 Au niveau des champs, qui correspondent aux colonnes de la table
 La configuration est implicite
 Les champs qui n’ont pas d’annotations sont par défaut mappés sur
des colonnes ayant le même nom qu’eux
 Si le nom de la colonne est différent, on peut le paramétrer avec
l’annotation @Column
 Si le champ ne doit pas être mappé, il faut le marquer avec
@Transient
 Dans le cas le plus simple, il suffit d’annoter la classe avec
@Entity et définir le champ contenant la clef primaire avec
@Id
228
Utilisation de ce mapping (1/2)
@Service
@Transactional
public class TodosServiceImpl implements TodosService {

@PersistenceContext
private EntityManager em;

public void createTodo(Todo todo) {


Date now = Calendar.getInstance().getTime();
todo.setCreationDate(now);
em.persist(todo);
}

public Todo findTodo(String todoId) {


return em.find(Todo.class, todoId);
}

public Todo updateTodo(String todoId, String description) {


Todo todo = em.find(Todo.class, todoId);
todo.setDescription(description);
}
public void deleteTodo(String todoId) {
Todo todo = em.find(Todo.class, todoId);
em.remove(todo);
}
}

229
Utilisation de ce mapping (2/2)
 Le PersistenceContext est la classe principale, qui permet
de requêter, créer ou supprimer des objets en base de
données
 La persistance est transparente : dès qu’un objet est géré
par le PersistenceContext, ses modifications seront
automatiquement répercutées en base de données à la fin
de la transaction
 Pour mettre à jour un objet, il suffit donc d’appeler ses
setters, et d’attendre la fin de la transaction

230
Configuration avec Spring (1/3)
 Il faut configurer un EntityManagerFactory, qui sera capable
de fournir les EntityManager utilisés précédemment

<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="database" value="MYSQL"/>
<property name="databasePlatform"
value="org.hibernate.dialect.MySQLDialect"/>
</bean>
</property>
<property name="persistenceXmlLocation"
value="classpath:META-INF/persistence.xml"/>
</bean>

231
Configuration avec Spring (2/3)
 La configuration Spring référence un fichier persistence.xml
 C’est le fichier standard de configuration de JPA
 Depuis Spring 3.1, ce fichier est optionnel
<persistence xmlns="...">
<persistence-unit name="default"
transaction-type="RESOURCE_LOCAL">
<class>tudu.domain.Todo</class>
<class>tudu.domain.TodoList</class>
<class>tudu.domain.User</class>
<properties>
<property name="hibernate.cache.region.factory_class"
value="net.sf.ehcache.hibernate.SingletonEhCacheRegionFactory"/>
</properties>
</persistence-unit>
</persistence>

 Préférer l’utilisation de la propriété dans la définition du


EntityManagerFactory
<property name="packagesToScan" value="com.howtodoinjava.demo.model" />

232
Configuration avec Spring (3/3)
 Pour que Spring puisse injecter un EntityManager, il faut lui
ajouter le BeanPostProcessor suivant :

<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />


OU
<context:annotation-config />
OU
<context:component-scan base-package="com.exemple.service" />

233
JPQL
 Java Persistence Query Language (JPQL) est un langage de
requêtage spécifique à JPA, qui est indépendant de la base
de données
 Il ressemble à SQL
 Mais il s’exécute sur les objets Java (et non les tables), et a
accès à leurs propriétés

SELECT user FROM User user where user.login LIKE :login

SELECT COUNT(user) FROM User user

234
Utilisation du EntityManager
 EntityManager permet de faire des requêtes en JPQL :
List<EmployeeEntity> employees = manager.createQuery("Select a From EmployeeEntity a",
EmployeeEntity.class).getResultList();

 Des requêtes de recherche par id simplifié :


manager.find(DepartmentEntity.class, id);
manager.getReference(EmployeeEntity.class, id);

 Des requêtes en sql :


List<Object[]> employees = manager.createNativeQuery("Select a .id, a.nom, a.prenom from
Employee_Entity a").getResultList();

 EntityManager gère également les création, modification


et suppression :
manager.persist(employee);
manager.merge(employee);
manager.remove(employee);
manager.createQuery("update Stock set stockName = 'DIALOG2‘ where stockCode =
'7277'").executeUpdate();
235
API Criteria
 L’API Criteria permet également de requêter la base via
JPA
 Elle évite de faire de la manipulation de chaînes de caractères
pour avoir la bonne requête
 Elle est très adaptée aux écrans de recherche, où l’on peut
sélectionner de nombreux critères différents
CriteriaBuilder qb = em. getCriteriaBuilder ();
CriteriaQuery<Todo> query = qb. createQuery (Todo.class);
Root from = query. from(Todo.class);
Predicate condition = qb. gt(from.get("priority" ), 20);
query.where(condition);
TypedQuery<Todo> typedQuery = em. createQuery (query);
List<Todo> todos = typedQuery. getResultList ();
 Peut être utilisée aussi pour construire des requêtes de mise à
jour ou de suppression

236
Gestion des Exceptions (1)
 De même que pour Spring JDBC, on peut ajouter un
BeanPostProcessor qui va gérer les Exceptions
 Il va également transformer les Exceptions JPA et Hibernate en
DataAccessException de Spring
 Il permet donc d’avoir la même hiérarchie d’Exceptions, quelle
que soit la technologie d’accès aux données utilisée : JDBC,
Hibernate ou JPA
 Au niveau de la couche «Service», l’implémentation de la
couche «Repository» n’est donc pas exposée

237
Gestion des Exceptions (2)
 Pour configurer cette gestion des Exceptions, il faut utiliser
un Aspect fourni par Spring
 Cet Aspect catche les Exceptions lancées par tous les Beans
annotés @Repository
 La configuration de cet Aspect passe par l’utilisation d’un
BeanPostProcessor
 Rappel : les BeanPostProcessor permettent de modifier les instances des
Beans Spring, après leur instanciation
@Repository
public class ExampleDaoImpl implements ExampleDao {
// méthodes
}

<!-- Aspect à ajouter dans la configuration des DAOs -->


<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

238
HibernateTemplate et JpaTemplate
 Il existe des classes HibernateTemplate et JpaTemplate
pour faciliter l’utilisation d’Hibernate et de JPA
 Exactement le même mécanisme que pour Spring JDBC et que
pour les transactions
 Cependant ces classes ne sont plus utiles avec les
versions récentes de Spring
 Spring utilise les APIs internes d’Hibernate pour gérer la
session Hibernate et les transactions
 Spring utilise des BeanPostProcessor spécifiques pour injecter les
EntityManager et gérer les Exceptions
 Il n’est donc pas conseillé d’utiliser ces Templates
 Mieux vaut coder des classes JPA «normales», sans dépendance
sur Spring

239
Gestion des transactions
 Avec JPA, comme en Spring JDBC, la gestion des
transactions est essentielle
 C’est d’autant plus important qu’Hibernate flushe ses données
à la fin de la transaction
 JPA est généralement utilisé dans un serveur d’applications, et
donc avec un gestionnaire de transactions JTA, mais il a
également une implémentation dédiée si nécessaire :

<bean id="transactionManager"
class="org.springframework.orm.jpa.JpaTransactionManager" >
<property name="entityManagerFactory" ref="entityManagerFactory“ />
</bean>

240
Exercices

241
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Afin de réaliser cet exercice nous aurons besoin de:
 Projet Formation-Exercice7 qui est dans le dossier fournit
Addendum2
 Rappel de HSQLDB :
 Pour lancer le serveur :
 Aller dans la ligne de commande windows, dans le dossier hsqldb-
2.3.3\hsqldb
 Exécuter : java -classpath lib/hsqldb.jar
org.hsqldb.server.Server --database.0
file:hsqldb/DevisEnLigneDb --dbname.0 devisenligne
 Pour lancer le HSQL DataBase Manager : Exécuter le fichier
hsqldb-2.3.3\hsqldb\bin\runManagerSwing.bat

242
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Importer le projet Formation-Exercice7 dans Eclipse :
 Dans la vue Navigator d’Eclipse : click droit / import / Maven / Existing Maven Projects
 Ajouter les dépendances spring-orm, et hibernate-entitymanager
sous <dependencies>: dans le fichier pom.xml
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.0.7.Final</version>
</dependency>

 Si pas de connexion internet récupérer le contenu du dossier


addendum1/maven.repository et le mettre dans
lecteur:\Users\[user]\.m2\repository
 Ne pas oublier de mettre à jour le projet dans Eclipse après chaque modification du
pom.xml

243
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Configuration de l’accès à la base de données :
 La configuration de l’exercice précédent est gardée avec les
différences près suivantes :
 L’attribut transaction-manager de <tx-annotation-driven a disparu. Le
nom du bean de gestion des transactions est désormais indiqué au niveau
de l’annotation @Transactional
 L’attribut order order="1" apparait afin de donner la priorité supérieure
à l’aspect de gestion des transactions. Nous verrons plus loin pourquoi
nous avons besoin de cela.
<tx:annotation-driven order="1" />

 Les beans du model (classes du package ma.devisenligne.model) sont


désormais annotés avec les annotations JPA afin de configurer leurs
mappings O/R

244
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Dans dao-context.xml ajouter la configuration d’un bean
entityManagerFactory et d’un bean gestionnaire des transactions
adapté à JPA :
<bean id="entityManagerFactoryBean"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="ma.devisenligne.model" />
<property name="jpaVendorAdapter">
<bean
class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" /> Le gestionnaire des transactions
</property> pour Jdbc ne peut être utilisé avec
<property name="jpaProperties"> JPA, du coup nous allons configurer
<props>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
un autre avec JPA.; d’où le
<prop déplacement de la définition du nom
key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> du transaction manager dans les
</props> annotations @Transactional
</property>
</bean>

<bean id="jpaTxManager" class="org.springframework.orm.jpa.JpaTransactionManager">


<property name="entityManagerFactory" ref="entityManagerFactoryBean" />
</bean>

245
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Ajouter dans la couche DAO, l’accès à la façade EntityManager de JPA :
 Dans FournisseurDAO ajouter :
 Un attribut privé de type EntityManager
 Annoter cet attribut avec @PersistenceContext afin de permettre à Spring d’y injecter
l’implémentation Hibernate de EntityManager

@PersistenceContext
private EntityManager entityManager;
 Modifier l’implémentation de la méthode create(Fournisseur) pour persister le fournisseur
à créer dans la base de données :
 Utiliser la méthode getReference de EntityManager pour récupérer des proxy de TypeClient et
EtatCompte
 Utiliser la méthode persist de EntityManager pour insérer le fournisseur dans la base de données

if (frns.getTypeClient() == null) {
TypeClient type = entityManager.getReference(TypeClient.class, defaultClientTypeId);
frns.setTypeClient(type);
}
if (frns.getEtat() == null) {
EtatCompte etat = entityManager.getReference(EtatCompte.class, defaultFrnsEtatId);
frns.setEtat(etat);
}
System.out.println("Fournisseur créé : ");
entityManager.persist(frns);

246
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Regarder le code de la classe FrnsApplication.java
 Exécuter la classe FrnsApplication
 Si vous rencontrez une erreur, c’est normal, JPA exige que les méthodes
d’écriture en BD soient déroulées dans le cadre d’une transaction.
 Fixer ce problème en ajoutant l’annotation @Transactional sur la méthode
create de FrnsManager : @Transactional(value="jpaTxManager")
 Exécuter la classe FrnsApplication et vérifier que les fournisseurs ont été
créés dans la base de données : select * from fournisseur (HSQL DB
Manager)
 Implémenter la méthode findFrnsById (id) de FournisseurDAO
 Utiliser la méthode find de EntityManager :
return entityManager.find(Fournisseur.class, id);

 Exécuter la classe FrnsApplication


 Vérifier que le fournisseur avec l’id 1 a été bien récupéré.
 Avez-vous constaté que les informations EtatCompte et TypeClient de
Fournisseur sont bien renseignées dans la Base de données ?

247
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Implémenter la méthode findFournisseurs de
FournisseurDAO en utilisant la méthode createQuery de
EntityManager :
List<Fournisseur> frnss = entityManager.createQuery("Select a From Fournisseur a",
Fournisseur.class).getResultList();
return frnss;

 Constat : le code produit est beaucoup plus simple que


dans le cas d’utilisation de Jdbc.
 Exécuter FrnsApplication, constater la récupération de la
liste des Fournisseurs dans la base de données

248
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Modifier la config annotation des transactions dans dao-context.xml :
 Supprimer l’attribut « order »
 Exécuter FrnsApplication, constater ce qui se passe ! Quelle en est l’explication
 Provoquer une erreur dans la couche JPA :
 Dans FrnsApplication mettre un TypeClient qui n’existe en base de données (id 209 par exemple):
 TypeClient typeClient = new TypeClient();
 typeClient.setId(209);
 frns.setTypeClient(typeClient);
 Comme aucun type client avec l’id 209 n’existe dans la base de données et qu’il existe une contrainte
référentiel de Fournisseur sur TYPE_CLIENT Une exception sera levée
 Constater que l’exception levée est propre à JPA enveloppant une exception native de
Hibernate !!
 Configurer Spring pour qu’il traduise les exceptions de la couche DAO (ajouter le bean ci-
dessous dans context-dao.xml):
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />

 Relancer FrnsApplication
 Rappel : c’est l’annotation @Repository qui indique à Spring que nous voulons que les
exceptions soient encapsulées par celles de Spring.

249
Exercice 7 : Utilisation de Spring JPA avec
Hibernate pour les accès à une BD
 Exécuter ClientApplication :
 Constater que notre application met en jeux les deux apis
d’accès à la base de données sans problème.

 Ajouter l’annotation @Transactional(value="jpaTxManager") sur la


méthode addFrnss de FrnsManager
 Exécuter TransactFrnsApplication :
 Constater alors que le support transactionnel est bien présent.
S’il y a une erreur aucun fournisseur n’est créé..

250
Spring MVC

251
Introduction à Spring MVC
 Spring MVC est un framework Model-View-Controller
 Il permet de faire des applications Web
 Il est relativement simple à apprendre
 Il est performant et robuste
 Il est très configurable
 Spring MVC est intégré à Spring Core (ce n’est pas un
sousprojet)

252
Le modèle MVC
 Le modèle MVC a été popularisé en Java par Struts
 Spring MVC est un concurrent direct de Struts

253
Le modèle MVC (2)

254
Les composants MVC
 Dans tout Framework MVC, le développeur a besoin de
configurer :
 Le mapping des requêtes http vers les controllers qui se
chargent de construire le model et le renvoyer vers le
navigateur
 L’instruction de l’objet ou les paramètres de la requêtes http
pour les exploiter
 Le mécanisme de résolution des pages jsps (ou autres) qui
s’occupent de l’affichage du model

255
Les composants de Spring MVC
 Le Controller
 Un Bean Spring, annoté @Controller
 Supporte donc l’injection de dépendance : c’est ainsi qu’il accède à la
couche Service
 C’est lui qui valide la requête, remplit le modèle, et redirige vers la
bonne vue
 La vue
 C’est typiquement une JSP qui va être chargée d’afficher le modèle
 Spring MVC n’est pas limité à la technologie JSP, mais c’est elle qui est le
plus couramment utilisée
 Le modèle
 Un Java Bean ou un ensemble de Java Beans (Collection), provenant de la
couche service et étant affiché par la JSP
 Dans l’architecture que nous avons étudiée, ce Java Bean est
généralement une entité Hibernate, ou un ensemble d’entités Hibernate
 Il peut être validé par Bean Validation (cf. chapitre suivant)

256
Pré-requis : lancer Spring
 Avant de configurer et lancer Spring MVC, il faut configurer et
lancer Spring
 Il s’agit de configurer un listener de Servlet, dans le fichier web.xml
 Ce listener va lire le fichier de configuration Spring passé en
paramètre, et se charger de lancer le contexte Spring
 Comme nous l’avons vu dans les chapitres précédents, Spring va
alors trouver la Data Source, lancer JPA, gérer les transactions...
Nous aurons donc ainsi une application pleinement fonctionnelle

<context-param>
<param-name>contextConfigLocation </param-name>
<param-value>classpath:META-INF/spring/application-context.xml </param-value>
</context-param>

<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener </listener-class>
</listener>

257
Configuration de Spring MVC
 La classe principale est la Servlet nommée
DispatcherServlet
 Elle lance un Application Context Spring
 Elle lui transmet ensuite les requêtes HTTP
<servlet>
<servlet-name>dispatcher </servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet </servlet-
class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcher </servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>

258
Lancement du context Spring MVC
 Cette Servlet va rechercher un fichier de configuration Spring
 Par défaut, elle lit le fichier WEB-INF/${NOM_DE_LA_SERVLET}-servlet.xml
 Dans notre exemple, il s’agit donc de WEB-INF/dispatcher-servlet.xml
 Elle va alors instancier un contexte applicatif Spring, qui sera un enfant du contexte
applicatif principal
 Voir les Application Contexts hiérarchiques, dans le chapitre sur la configuration Spring
 Il est bien entendu possible de fournir un nom différent de fichier de config en
ajoutant à la config de la Servlet :
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/web-context.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

 De même qu’il est possible de n’avoir qu’un seul fichier de config pour tous les
contextes Spring (mvc et core)

259
Fonctionnement de Spring MVC avec
Spring

260
Fichier de configuration de Spring
MVC

<beans xmlns="...">
<context:component-scan base-package="exemple.test.web"/>
<mvc:annotation-driven/>

</beans>

261
Choix de la JSP en fonction de la vue
demandée
 En fonction du nom de la vue demandée, Spring MVC va
retrouver la JSP correspondante
 Généralement, il s’agit juste de mettre un préfixe et un
suffixe à la vue demandée
 La vue «account» correspond ainsi à la JSP «WEB-
INF/jsp/account.jsp»

<bean id="viewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver" >
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp"/>
</bean>

262
Configuration d’un Controller
 Un Controller Spring MVC est un Bean Spring
 On peut lui injecter des Beans Spring de la couche service, qui
sont dans l’Application Context Spring parent
 Il se configure uniquement par annotations
 Historiquement en XML
 Généralement, il n’a pas d’interface et on lui laisse son nom par
défaut : il n’est pas destiné à être injecté dans un autre Bean

263
Exemple de Controller
@Controller
@RequestMapping ("/account" )
public class AccountController {

@Inject
private UserService userService;
@RequestMapping (method = RequestMethod. GET)
public ModelAndView display() {
User user = userService. getCurrentUser ();
ModelAndView mv = new ModelAndView( "account" );
mv. addObject ("user", user);
return mv;
}

@RequestMapping (method = RequestMethod. POST)


public String update( @RequestParam String name) {
userService. updateName (name);
return "account_ok" ;
}
}
264
Mapping des requêtes
 Les requêtes peuvent être mappées au niveau de la classe
ou des méthodes
 Le mapping s’effectue sur un chemin, mais peut aussi être
fait sur d’autres critères comme en fonction du verbe
HTTP utilisé
 GET pour lire une donnée
 POST pour la mettre à jour
 Ou sur le MediaType à consommer ou à produire :
 text/plain
 application/json
 application/xml

265
Gestion des paramètres passés à la
requête
 La manière la plus simple : ajouter à la méthode un
argument annoté avec @RequestParam
 Par défaut, ce paramètre est obligatoire et a le même
nom que le nom de la variable
public String update( @RequestParam String name) {...}

 Cela est bien entendu paramétrable :

public String update(@RequestParam (value="userName" , required= "false") String name) {...}

266
@ModelAttribute sur une méthode
(1/2)
 La première utilisation de @ModelAttribute est de mettre
dans le modèle un objet métier
 Cet objet peut être une entité JPA
 Il peut être également un objet spécifiquement développé pour
la vue
 Lorsqu’une méthode est annotée avec @ModelAttribute,
cette méthode est appelée avant chaque mapping
(méthodes annotées @RequestMapping)
 Ainsi, on est certain d’avoir les bonnes données dès le début
 Attention : cela peut avoir un impact de performances, surtout
si cet objet n’est pas utilisé dans toutes les méthodes annotées
@RequestMapping

267
@ModelAttribute sur une méthode
(2/2)
 Dans cet exemple, un objet de type User sera stocké dans
le modèle, avec la clef «user» :

@ModelAttribute("user")
public User formBackingObject() {

return userService.getCurrentUser();
}

268
@ModelAttribute sur un paramètre de
méthode
 Sur un paramètre d’une méthode annotée
@RequestMapping, l’annotation @ModelAttribute va
permettre de «binder» les paramètres de la requête
HTTP sur cet objet

@RequestMapping(method = RequestMethod.POST)
public String register(@ModelAttribute("user") User user,
BindingResult result) {
if (result.hasErrors()) {
return "register";
}
return "register_ok";
}

269
L’objet ModelAndView
 L’objet ModelAndView est retourné par les méthodes du Controller
 Comme son nom l’indique, il représente le modèle et la vue du pattern
MVC
 Le modèle est un objet ModelMap, qui est une Map améliorée
 Les objets mis dans cette Map se retrouvent en attributs dans la requête
HTTP
 Si le Controller ne retourne qu’une String, il s’agit de la vue à
renvoyer, avec un modèle vide
ModelAndView mv = new ModelAndView();
mv.setViewName("account");
mv.addObject("user", user);

ModelAndView mv = new ModelAndView("account");


mv.addObject("user", user);

270
La Tag Library de Spring MVC
 Côté vue, Spring MVC propose une Tag Library pour
gérer les formulaires
 Cela permet de binder les données du formulaire HTML
avec les méthodes et les paramètres annotés
@ModelAttribute
<form:form modelAttribute="user">
<form:input path="firstName" size="15" maxlength="60"/><br/>
<form:input path="lastName" size="15" maxlength="60"/><br/>
<form:select path="sex">
<form:option value="M">M</form:option>
<form:option value="F">F</form:option>
</form:select><br/>
<input type="submit" value="Save Changes" />
</form:form>

271
Spring MVC et Bean Validation (1/2)
 Dans la configuration de Spring MVC, la ligne suivante
permet de prendre en compte Bean Validation :
<mvc:annotation-driven/>

 Ensuite, tout paramètre annoté @Valid sera validé par


Bean Validation avant binding
@RequestMapping(method = RequestMethod.POST)
public String register(@Valid User user, Errors
errors) {
if (errors.hasErrors()) {
return "register";
}
return "register_ok";
}

272
Spring MVC et Bean Validation (2/2)
 Cette configuration permet d’utiliser Bean Validation, qui
est un standard, pour valider les formulaires HTML
 Dans ce cadre, on aura certainement des Java Beans spécifiques
pour gérer les formulaires, comme les FormBeans de Struts
 Elle permet également de valider automatiquement les
objets de domaines que l’on afficherait directement dans
la page
 Cela reprend les principes d’objets de domaine «forts» qui
parcourent toutes les couches de notre architecture : on peut
maintenant directement interagir avec ces objets dans la page
HTML

273
Qu’est-ce que Bean Validation ?
 Bean Validation est une spécification (JSR-303 et JSR 349)
 L’implémentation de référence est Hibernate Validator, un sous-projet Hibernate
 Bean Validation permet de valider des Java Beans par l’ajout d’annotations
 Cela permet de «renforcer» les entités Hibernate
 Ces entités ne se contentent plus de stocker simplement des données, elles peuvent les valider
 Bean Validation est également utilisable en dehors d’Hibernate

@Entity
public class User implements Serializable {
@Id
@NotNull
private int id;
@Size(min = 0, max = 150)
@Email
private String email;
@Past
private Date creationDate;
@Min(0)
@NotNull
private int balance;
// getters et setters
}

274
Exemples d’annotations fournies
 @Size(min=2, max=240)
 @AssertTrue / @AssertFalse
 @Null / @NotNull
 @Max / @Min
 @Future / @Past
 @Digits(integer=6, fraction=2)
 @Pattern(regexp="\\(\\d{3}\\)\\d{3}-\\d{4}")

275
Hibernate Validator
 Hibernate Validator est l’implémentation de référence
 Hibernate Validator propose un certain nombre
d’annotations qui ne sont pas présentes dans la
spécification :
 @Email
 @URL
 @CreditCardNumber
 @Range

276
Configuration et utilisation
 L’intégration de Bean Validation est automatique avec JPA ! Il
n’y a donc rien à faire
 Lorsqu’une entité est créée ou modifiée, Bean Validation va
automatiquement la valider
 En cas d’erreur, Bean Validation lancera une Exception de type
ConstraintViolationException
 Cette Exception fournit un Set de ConstraintViolation, lesquelles
fournissent tous les détails sur les contraintes enfreintes (message
d’erreur, nom de la contrainte, champ, etc...)
 A priori, cette Exception va être traitée par la couche de présentation
 Il est possible de désactiver la validation à ce niveau en
ajoutant la config suivante dans la définition du
EntityManagerFactoryBean :
 <prop key="javax.persistence.validation.mode">none </prop>

277
Utilisation programmatique de Bean
Validation
 On peut également choisir de valider un Bean via l’API de
Bean Validation
 Si ce Bean n’est pas une entité Hibernate
 Parce que l’on se trouve dans une autre couche de l’application :
c’est la technique qui est utilisée implicitement dans Spring MVC
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<Book>> constraintViolations = validator.validate(book);

for (ConstraintViolation<Book> constraintViolation : constraintViolations) {


System.out.println(constraintViolation.getPropertyPath() + " - " +
constraintViolation.getMessage());
}

 Ces objets sont gérés par Spring et peuvent être injectés dans les
beans qui ont besoin du traitement de validation

278
Faire ses propres annotations de
validation
 Il est possible de créer des annotations spécifiques, qui
correspondent à un besoin ou à un métier spécifique
 Exemple : valider un numéro ISBN (code utilisé pour identifier
les livres)
public class Book {
@NotNull
@Isbn
private String isbn;
@Size(min = 1, max = 1024)
private String title;
@NotNull
private Author author;
// getters et setters
}

279
Faire ses propres annotations de
validation (2)
@Constraint (validatedBy = IsbnValidator. class)
@Target(value = ElementType. FIELD)
@Retention (RetentionPolicy. RUNTIME)
public @interface Isbn {
String message() default "Mauvais numéro ISBN" ;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default { };
}

import org.apache.commons.validator.routines.ISBNValidator ;
public class IsbnValidator implements ConstraintValidator<Isbn, String> {
public void initialize (Isbn constraintAnnotation) {
}
public boolean isValid(String value, ConstraintValidatorContext
context) {
return ISBNValidator. isValidISBN13 (value);
}
}

280
Les groupes de validation
 On ne veut pas toujours valider une entité dans son ensemble
 C’est en particulier utile dans la couche de présentation : il faut peut être deux étapes pour avoir une
entité complète
 On utilise alors des groupes de validations
 Il peut y avoir plusieurs groupes, et on peut valider plusieurs groupes en une fois
 Le groupe par défaut s’appelle «Default»
 Il faut pour cela créer une interface qui correspond à ce groupe
 Exemple de groupe de validation
public interface MinimalBook {}

public class Book {


@NotNull (groups = {MinimalBook.class, Default.class})
@Isbn
private String isbn;
@Size(min = 1, max = 1024)
@NotNull (groups = MinimalBook.class)
private String title;
@NotNull
private Author author;
// getters et setters
}

281
Conclusion sur Spring MVC
 Spring MVC est un framework MVC simple et puissant
 Il est facile d’accès et très performant
 Il se configure entièrement grâce à Spring
 Il utilise son propre Application Context
 Il est très souple et très paramètrable
 Cela peut être déroutant, et fait que l’on a parfois des
configurations très complexes
 Il s’intègre avec Bean Validation pour valider les
formulaires

282
Annexes
 Annotations
 Introduction à Maven
 Transactions

283
Annotations
 Java a toujours proposé une forme ou une autre de méta
programmation
 Depuis toujours, l'outil "javadoc" permettait d'exploiter
automatiquement des méta-données à but documentaire
/**
* Méthode inutile
* @param param Un paramètre (non utilisé)
* @return Une valeur fixe : "foo"
* @throws Exception N'arrive jamais (promis!)
*/
public String foo(String param) throws Exception { return "foo"; }

 Ce système était flexible et a rapidement été utilisé / détourné


pour générer d'autres artefacts : fichiers de configuration,
classes annexes :
 Constitution de fichier de config struts-config.xml ou encore
hbm.xml

284
Annotations
/**
* Struts Action for Login
*
* @struts.action path="/login" name="loginForm" input="/login.jsp" scope="request"
* @struts.action-forward name="form" path="/login.jsp"
* @struts.action-forward name="welcome" path="/welcome.do" redirect="true"
* @struts.action-forward name="changerMdp" path="/AfficherMotDePasse.do"
*/
public class LoginAction extends Action { ..

/**
* @hibernate.class table="EMPLOYE"
* @hibernate.cache usage="read-write"
*/
public class Employe implements Serializable {
..
..
/**
* @hibernate.property column="EMP_ACTIF"
*/
public int getEmpActif() {
return empActif;
}

285
Annotations
 Les cas précédents utilisaient un utilitaire Xdoclet pour
générer à partir des fichiers source java, les fichiers de
configurations nécessaires
 Reconnaissant le besoin d'un système de méta-programmation
plus robuste et plus flexible, Java 5.0 introduit les Annotations
 Java SE propose assez peu d'annotations en standard
 @Deprecated, @Override, @SuppressWarnings
 4 méta-annotations dans java.lang.annotation
 Celles définies par JAXB et Commons Annotations
 Java EE en fournit une quantité impressionnante
 Pour les EJB 3, les Servlets 3, CDI, JSF 2, JPA...
 Les Frameworks modernes en tirent également parti
 Spring, Hibernate, CXF...
 Développez les vôtres !

286
Annotations
 Les annotations peuvent être apposées sur les éléments suivants :
 Les classes, interfaces et enums
 Les propriétés de classes
 Les constructeurs et méthodes
 Les paramètres des constructeurs et méthodes
 Les variables locales
 … et d'autres éléments encore grâce à la JSR 308 (Java 8)
 Les annotations peuvent prendre des paramètres
 Ils peuvent être obligatoires ou facultatifs (ils prennent alors la valeur par
défaut spécifiée par le développeur de l'annotation)
 Les paramètres se précisent entre parenthèses, à la suite de l'annotation
; leur ordre n'est pas important :
 @MyAnnotation( param1=value1, param2=value2... )
 Si l'annotation ne prend qu'un paramètre, il est souvent possible
d'utiliser une notation raccourcie :
 @MyAnnotation( value )

287
Annotations
 C’est bon de les avoirs mais qu’est ce qu’on peut faire avec :
 Au niveau compilation :
 Génération de ressources : "XDoclet ++«
 Fichiers de configuration
 Classes annexes
 Ex: Proxies, PropertyEditors...
 Documentation
 Ex: Matrice des rôles JavaEE, cartographie d'IOC...
 Amélioration du compilateur
 Vérification de normes de codage
 Ex: Les classes "modèle" doivent être Serializable...
 Messages d'alerte ou d'erreur supplémentaires
 A l’exécution (Runtime) :
 Utiliser la Réflexion pour découvrir les annotations présentes sur les classes,
champs, méthodes et paramètres de méthodes.
 Use-cases :
 Mapping ORM
 Programmation orientée POJO
 Configuration de containers / frameworks
 Exemples : Hibernate, Apache CXF, Spring ..

288
Développer vos propres annotations
@Target( {ElementType.TYPE, ElementType.METHOD}
@Retention( RetentionPolicy.RUNTIME )
public @interface MyAnnotation {
String message();
int answer() default 42;
}

 Valeurs possibles pour l'enum ElementType :


 TYPE , CONSTRUCTOR, FIELD, METHOD, PARAMETER,
LOCAL_VARIABLE..
 Valeurs possibles pour l'enum RetentionPolicy :
 SOURCE
 CLASS (par défaut)
 RUNTIME

289
Exploiter vos propres Annotations
 Au niveau compilation (Retention policy Source ou class) :
 Définir un processeur d’annotation qui va réaliser le traitement
désiré sur les instances d’une annotation particulière
 Une classe implémentant l'interface javax.annotation.processing.Processor ou sous-
classer AbstractProcessor :
 L'annotation @SupportedAnnotationTypes permet d'indiquer quelles
annotations le processeur sait traiter
 La méthode init() permet d'initialiser le processeur
 La méthode principale process() reçoit un paramètre de type
RoundEnvironment représentant l'environnement de compilation. Elle
renvoie un booléen indiquant si l'annotation est définitivement
"consommée" par ce processeur
 Au niveau exécution (Retention policy Runtime) :
 Utiliser la reflection dans Java pour récupérer les éléments (classe,
méthode ou autres)
 Appliquer des traitements dessus

290
Annexes
 Annotations
 Introduction à Maven
 Transactions

291
Introduction à Maven
 Apache Maven est un logiciel de gestion de projets et un
outil de construction, basé sur :
 le concept du POM (Project Object Model)
 le respect de conventions et normes standards
 Elément central dans la gestion d'une infrastructure de
projet informatique :
 construction du livrable final
 gestion des bibliothèques logicielles de dépendances,
 génération de la documentation et rapport du projet
 déploiement des versions de livraison sur les plateformes
cibles

292
Introduction à Maven
 L'objectif recherché est de :
 produire un logiciel à partir de ses sources,
 en optimisant les tâches réalisées à cette fin
 et en garantissant le bon ordre de fabrication.
 Compiler, Tester, Contrôler, produire les packages livrables
 Publier la documentation et les rapports sur la qualité
 Apports :
 Simplification du processus de construction d’une application
 Fournit les bonnes pratique de développement
 Tend à uniformiser le processus de construction logiciel
 Vérifier la qualité du code
 Faciliter la maintenance d’un projet

293
Introduction à Maven
 Make est un logiciel qui construit automatiquement des
fichiers, souvent exécutables, ou des bibliothèques à partir
d'éléments de base tels que du code source.
 Ant est un logiciel créé par la fondation Apache qui vise à
automatiser les opérations répétitives du développement de
logiciel telles que la compilation, la génération de documents
(Javadoc) ou l'archivage au format JAR.
 Maven utilise un paradigme connu sous le nom de Project
Object Model (POM) afin de :
 Décrire un projet logiciel,
 Ses dépendances avec des modules externes
 et l'ordre à suivre pour sa production.
 Il est livré avec un grand nombre de tâches (GOALS) prédéfinies,
comme la compilation du code Java ou encore sa modularisation.

294
Introduction à Maven
 Descripteur du projet au format XML / 4 éléments
obligatoires :
 modelVersion : précise la version du modèle objet utilisé
 groupId : identifie le projet dans un espace de nommage
 artifactId : correspond à la valeur principale contenue dans le
nom du fichier de sortie construit par le projet (fichier XML
ou archive)
 version : donne la version en cours du projet

<project xmlns="http://maven.apache.org/POM/4.0.0" >


<modelVersion>4.0.0</modelVersion>
<groupId>com.mgreau.mvnbook</groupId>
<artifactId>mvnbook-persistence</artifactId>
<version>1.0.0-SNAPSHOT</version>
</project>

295
Introduction à Maven
 Référentiels (repositories) – Gestion des artefacts
 Référentiel local (local repository)
 Référentiels distants (remote repositories)
 Fichiers settings.xml - Configuration
 settings.xml local : ${user.home}/.m2/settings.xml
 settings.xml global : $M2_HOME/conf/settings.xml
 Structure de projet standard
${project.basedir}/src/main/java
${project.basedir}/src/main/resources
${project.basedir}/src/test/java
${project.basedir}/src/test/resources p
${project.basedir}/src/main/scripts
${project.basedir}/target
${project.basedir}/target/classes
${project.basedir}/target/test-classes
${project.basedir}/target/site
pom.xml

296
Introduction à Maven
 Dépendances :
 Identifier et importer des référentiels distants vers le local les
bibliothèques logicielles (dépendances) nécessaires au
fonctionnement du projet
 Prend en compte la transitivité des dépendances
 Permet d’indiquer le scope de chaque dépendance :
 Compile : valeur par défaut, indique que la dépendance est nécessaire
pour la compilation et du coup elle sera sur le classpath du projet
 Provided : similaire à compile à la différence que dans ce cas la
dépendance n’a pas besoin d’être packagée avec le projet
 Runtime : la dépendance n’est pas requise pour la compilation mais pour
l’exécution
 Test : la dépendance est nécessaire pour les tests
 System : similaire à Provided à la différence que dans ce cas la dépendance
est fournie explicitement; elle n’est pas cherché dans un repository

297
Introduction à Maven – cycle de vie
 23 phases pour ce cycle
 Chaque type de projets (JAR, EAR, WAR...) définit les goals à exécuter et
les associe à des phases
 Cycle de vie par défaut pour la construction d'un JAR

 L’exécution d’une tâche d’une phase entraine l’exécution des tâches


de toutes les phases précédentes

298
Introduction à Maven
 5 catégories
 1 Super POM Hérité par tous les POM

<project>
<modelVersion>4.0.0</modelVersion>
<repositories>
<repository>
<id>central</id>
<name>Maven Repository Switchboard</name>
<url>http://repo1.maven.org/maven2</url>
...
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>central</id>
<name>Maven Plugin Repository</name>
<url>http://repo1.maven.org/maven2</url>
...
</pluginRepository>
</pluginRepositories>

</project>

299
Annexes
 Annotations
 Introduction à Maven
 Transactions

300
Introduction à Maven
 Introduit la notion de profil afin d’automatiser et faciliter
les déploiements dans des environnements différents
 Utilisation de deux façons :
 Ligne de commande :
 mvn compile
 mvn test
 mvn install
 Plugin Eclipse :
 qui permet de créer des projets Maven
 Modifier le fichier pom.xml dans l’interface graphique
 Lancer les tâches de build

301
Transactions et isolation
 Dans la pratique, les transactions ne sont en réalité pas
toujours bien isolées
 Il y a quatre niveaux d’isolation, du plus sécurisé au moins
sécurisé :
 SERIALIZABLE
 REPEATABLE READS
 READ COMMITTED
 READ UNCOMMITTED
 Plus le niveau d’isolation est élevé, plus la base doit locker des
ressources, et moins les performances sont bonnes
 Le niveau d’isolation par défaut est configurable dans la base
de données, sous Oracle il est à «READ COMMITTED»

302
Exemple 1 : non repeatable read
 Ce problème arrive lorsque l’on est en READ
COMMITTED ou READ UNCOMMITTED

303
Exemple 2 : phantom read
 Ce problème arrive lorsque l’on est en REPEATABLE
READ, READ COMMITTED ou READ UNCOMMITTED

304
Les transactions read-only
 On peut marquer une transaction comme étant «read-
only»
 On indique qu’elle ne va pas modifier de données en base
 En lecture, il est toujours important d’utiliser des transactions,
ne serait-ce que pour les performances
 Cet attribut est important
 Hibernate va le comprendre, il ne va alors plus vérifier s’il doit
impacter des modifications sur les objets en base : meilleures
performances
 Certains drivers JDBC vont le comprendre (Oracle ne vous
autorisera plus qu’à faire des «SELECT») : meilleure qualité

305
La propagation des transactions 1
 Que se passe-t-il quand une méthode transactionnelle en
appelle une autre ?

306
La propagation des transactions 2
 On peut configurer si l’on veut deux transactions différentes ou une
seule transaction englobant les deux méthodes
 REQUIRED : S’il y a déjà une transaction, l’utiliser. Sinon, en créer une
nouvelle. C’est le mode par défaut.
 REQUIRES_NEW : Crée toujours une nouvelle transaction. S’il y en a
déjà une, la suspend.
 NESTED : Peut faire une transaction imbriquée, si cela est supporté par
le gestionnaire de transaction.
 MANDATORY : Une transaction doit déjà exister. Sinon, lance une
Exception.
 SUPPORTS : Si une transaction existe déjà, l’utilise. Sinon, n’utilise pas
de transaction.
 NOT_SUPPORTED : N’utilise pas de transaction. Si une transaction
existe déjà, la suspend.
 NEVER : N’utilise pas de transaction. Si une transaction existe déjà,
lance une Exception.

307
Pattern «Open Transaction in View»
 C’est un pattern très répandu dans les applications Web, aussi
appelé «Open Session In View» (la session étant une session
Hibernate)
 Spring propose un listener de Servlet qui implémente ce pattern
 Ce pattern est très pratique
 Permet d’avoir une transaction ouverte tout le temps, y compris
dans les pages Web
 Règle les problèmes de «lazy loading» avec Hibernate
 Nous le déconseillons parfois pour des raisons de
performances
 Il a tendance à laisser les transactions ouvertes trop longtemps
 On arrive à terme à une requête HTTP == une transaction, et donc
à une connexion en base de données. Difficile alors de monter en
charge !

308
Entete xml – application-context.xml

<?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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd">

309