SPRING Master Informatique 2ème année 2011-2012 Michael Fortier fortier@univ-paris13.fr 1
SPRING Concepts Bean Injection Of Control (IoC) Exemple classique Aspect Oriented Programming (AOP) Data Access Object (DAO) Jdbc Hibernate (*) TP : Application complète (gestion location de voitures) 2
SPRING CONCEPTS 3
SPRING - Concepts J2EE Spécification Manques (EJB1-2) Lourdeurs Difficultés (sécurité) Spring pour pallier Robustesse Extensibilité Réutilisabilité Best practices 4
SPRING - Concepts Framework de développement Java basé sur la notion de conteneur léger V3.* Open Source, communauté SpringSource Modulaire "Chaque brique est indépendante" 5
SPRING - Concepts Boite à tout faire JSP / Servlet JSF Struts Grails RMI JUnit JDBC / Hibernate... "Conteneur léger" Pas d'implémentation d'interface nécessaire ~ infrastructure d'un serveur d'application JEE Interopérabilité frameworks 6
SPRING - Concepts Super fabrique d'objets qui permet de gérer : Le mode transactionnel L'appel/création d'ejbs La persistance des objets Interface Web et WebServices 7
SPRING - Concepts La pile de services 8
SPRING - Concepts Applications Web n-tiers Interface utilisateur Métier Moteur de servlets Spring Moteur de servlets DAO 9
SPRING - Concepts Applications Serveur / Client lourd Client lourd Métier DAO Spring Spring remoting Spring Moteur de servlets 10
SPRING - Concepts Applications réparties Client lourd Métier DAO Spring Spring remoting Spring Spring remoting Spring 11
SPRING - Concepts EJB Client Spring EJB Spring Serveur d'applications 12
SPRING BEAN 13
SPRING BEAN BeanFactory Fichier de configuration Types de Bean <beans> <!-- Première fabrique--> <bean id="produitdao" class="product.dao.produitdaoimpl"></bean> <!-- Seconde fabrique--> <bean class="produit"> <property name="produitdao"> <ref bean="produitdao"/> </property> </bean> </beans> Singletons création d'une instance unique à l'initialisation (par défaut) Prototypes création à la demande (lors d'un appel type : «springfactory».getbean(«product»)) 14
SPRING BEAN Propriétés déclarer un besoin Références vers autre bean (IoC) Valeurs par défaut Paramètres de configuration Initialisations de tableaux/listes 15
SPRING BEAN Propriétés : Exemple d'initialisation <beans> <bean id="personne1" class="metier.personne" init-method="init" destroy-method="close"> <property name="nom"> <value>bob</value> </property> <property name="age"> <value>40</value> </property> </bean> <bean id="personne2" class="metier.personne" init-method="init" destroy-method="close"> <property name="nom"> <value>roger</value> </property> <property name="age"> <value>20</value> </property> </bean> </beans> ApplicationContext.xml 16
SPRING BEAN Propriétés : Exemple d'hibernate <bean id="derbydatasource" class="org.springframework.jdbc.datasource.drivermanagerdatasource"> <property name="driverclassname"> <value>org.apache.derby.jdbc.clientdriver</value> </property> <property name="url"> <value>jdbc:derby://localhost:1527/location</value> </property> <property name="username"> <value>user</value> </property> <property name="password"> <value>mdp</value> </property> </bean> <bean id="masessionfactory" class="org.springframework.orm.hibernate.localsessionfactorybean"> <property name="mappingressources"> <list> <value>exemple.hbm.xml</value> </list> </property> </bean> 17 ApplicationContext.xml
SPRING BEAN Application Java ClassPathResource res = new ClassPathResource("applicationContext.xml"); XmlBeanFactory factory = new XmlBeanFactory(res); ProduitServices vservices =(ProduitServices)factory.getbean("monProduitServices"); Application Web <context-param> <param-name>contextconfiglocation</param-name> <param-value>/web-inf/applicationcontext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.contextloaderlistener </listener-class> </listener> Web.xml 18
SPRING BEAN Récupérer un bean ClassPathResource res = new ClassPathResource("applicationContext.xml"); // La fabrique SPRING est chargée, les singletons sont créés ListableBeanFactory factory = new XmlBeanFactory(res); // On utilise la méthode getbean en passant le nom du bean pour créer // ou récupérer un bean déclaré dans le fichier de configuration Personne p = (Personne)factory.getbean("personne1"); 19
SPRING INJECTION OF CONTROL 20
SPRING IOC IOC : Inversion Of Control (injection de dépendance) Design Pattern casser les dépendances Se base sur la notion d'abstraction commune (interface java) le framework qui a la charge d'injecter les dépendances nécessaires 21
SPRING IOC IOC : Exemple simple 22
SPRING IOC IOC : Comprendre l'idée package factory; package product; import factory.daofactory; import product.dao.productdao; public class Product{ private Produit dao; private long id; private String name; private String description; public Product(){ Dao = (Produit)DAOFactory.getFactory("ProductDAO"); import product.dao.productdaoimpl; import product.dao.clientdaoimpl; import product.dao.commanddaoimpl; import java.util.*; public class DAOFactory { private Hashtable factories; private static DAOFactory self = null; protected DAOFactory(){ factories = new Hashtable(); factories.put("productdao", new ProductDAOImpl()); factories.put("clientdao", new ClientDAOImpl()); factories.put("commanddao", new CommandDAOImpl()); public String getname(){return name; //etc Appel à la classe DAOFactory Implémentations des différents DAO déclarés en dur public static Object getfactory(string factoryname) throws NullPointerException { return DAOFactory.self().get(factoryName); protected Object get(string factoryname) throws NullPointerException { return factories.get(factoryname); public static synchronized DAOFactory self(){ if(self == null){ self = new DAOFactory(); return self; 23
SPRING IOC IOC : Comprendre l'idée Problèmes : - Comment tester quand les classes implémentant les DAO ne sont pas développées? - Faire fonctionner des classes DAO différentes suivant le contexte d'exécution (développement, production...) 24
SPRING IOC IOC : Comprendre l'idée package product; Solution import factory.daofactory; import product.dao.productdao; public class Product{ private ProductDAO dao; private long id; private String name; private String description; public void setproductdao(productdao dao){ this.dao = dao; public String getname(){return name; Fichier de configuration Spring : <beans> <!-- Première fabrique--> <bean id="productdao" class="product.dao.productdaoimpl"></bean> <!-- Seconde fabrique--> <bean class="product.product"> <property name="productdao"> <ref bean="productdao"/> </property> </bean> //etc </beans> 25
SPRING IOC 3 types d'injection par constructeur Object construitcomposant(string pnom){ Class c=recherchelaclassquiimplemente(pnom) ; String[] dep= recherchelesdependance(pnom) ; Params[] parametresdeconstructeur; Pour chaque element (composant) de dep Faire Object o= construitcomposant(composant) ; Rajouter o à la liste de parametresdeconstructeur ; Fin Faire construireclasse( c, parametresdeconstructeur) 26
SPRING IOC 3 types d'injection par mutateurs (setter) Object construitcomposant(string pnom){ Class c=recherchelaclassquiimplemente(pnom) ; Object composant=new c() ; String[] dep= recherchelesdependance(pnom) ; Params[] parametresdeconstructeur; Pour chaque element (composant) de dep Faire Object o= construitcomposant(composant) ; composant.setnommembre(o) ; Fin Faire 27
SPRING IOC 3 types d'injection interface Object construitcomposant(string pnom){ Class c=recherchelaclassquiimplemente(pnom) ; Object composant=new c() ; String[] dep= recherchelesdependance(pnom) ; Params[] parametresdeconstructeur; Pour chaque element (composant) de dep Faire Object o= construitcomposant(composant) ; composant.méthodeinjection(o) ; Fin Faire public interface IInjectMethode{ public void méthodeinjection(object o) ; 28
SPRING EXEMPLE CLASSIQUE 29
SPRING EXEMPLE CLASSIQUE Le cas d'une gestion de stock d'articles public class Stock{ // Déduis une quantité du stock d'articles void sortarticledustock(string particleid, int quantite, java.sql.connection pconn){ try{ String sql ="update Articles a set a.quantite=a.quantite-" + quantite + " where a.articleid='" + particleid + "'"; Statement statement=pconn.createstatement(); statement.executestatement(sql); catch(exception e){ e.printstacktrace(); finally{ if(statement!= null){ try{ statement.close(); catch(exception ex){ ex.printstacktrace(); 30
SPRING EXEMPLE CLASSIQUE Problèmes : Les tests ne sont pas effectués (article inexistant, quantité négative...) code peu résistant Maintenabilité? Testabilité? 31
SPRING EXEMPLE CLASSIQUE Améliorations : public class Stock{ void sortarticledustock(string particleid, int pqty, java.sql.connection pconn) throws ArticleNotFoundExeception, QtyNegativeException(){ try{ if(pqty<0){ thrown new QtyNegativeException(); sql ="select 'x' from Articles a where a.articleid='" + particleid + "'"; Statement statement=pconn.createstatement(); Resultset resultset=statement.executequery(sql); if(resultset==null!resultset.next()){ thrown new ArticleNotFoundException(); String sql ="update Articles a set a.quantite=a.quantite-" + quantite + " where a.articleid='" + particleid + "'"; statement=pconn.createstatement(); statement.executestatement(sql); catch(sqlexception e){ e.printstacktrace(); finally{ if(statement!= null){ Try{ statement.close(); catch(sqlexception ex){ ex.printstacktrace(); 32
SPRING EXEMPLE CLASSIQUE Spring découpler les couches métiers et accès aux données // Interface pour gérer la couche métier public interface IStockBusiness{ void sortarticledustock(string particleid, int quantite) throws ArticleNotFoundExeception, QtyNegativeException(); // Interface pour gérer la couche d'accès aux données public interface IStockDao{ void sortarticledustock(string particleid, int quantite); boolean ArticleExist(String particleid); 33
SPRING EXEMPLE CLASSIQUE // Implémentation de la classe d'accès aux données class StockDAO implements IStockDao extend JdbcDaoSupport{ DataSource mdatasource; public setdatasource(datasource pdatasource){ mdatasource=pdatasource; boolean articleexist(string particleid){ String sql = "select count(*) from Articles a where a.articleid='" + particleid + "'"; int count = jt.queryforint(sql); return count > 0? true : false; void sortarticledustock(string particleid, int pqty){ String sql = "update Articles a set a.quantite=a.quantite-" + quantite + " where a.articleid=' + particleid + "'"; getjdbctemplate().execute(sql); 34
SPRING EXEMPLE CLASSIQUE // Implémentation de la classe métier class StockBusiness implements IStockBusiness{ IStockDao mdao; public setdao(istockdao pdao){ mdao=pdao; void sortarticledustock(string particleid, int quantite) throws ArticleNotFoundException, QtyNegativeException(){ if(quantite<0){ thrown new QtyNegativeException(); if(!mdao.articleexist(particleid)){ thrown new ArticleNotFoundException(); mdao.sortarticledustock(); boolean articleexist(string particleid){ return mdao.articleexist(particleid); 35
SPRING EXEMPLE CLASSIQUE <?xml version="1.0" encoding="utf-8"?> <!-- Fichier de configuration de Spring --> <beans> <bean id="proddatasource" class="org.apache.commons.dbcp.basicdatasource"> <property name="driverclassname"><value>org.postgresql.driver</value></property> <property name="url"><value>jdbc:postgresql://localhost:5432/prod</value></property> <property name="username"><value>prod</value></property> <property name="password"><value>toto</value></property> </bean> <bean id="stockdao" class="stockdao"> <property name="datasource"> <ref local="proddatasource"/> </property> </bean> <bean id="stockbusiness" class="stockbusiness"> <property name="dao"> <ref local="stockdao"/> </property> </bean> </beans> 36
SPRING ASPECT ORIENTED PROGRAMMING 37
SPRING AOP AOP : Aspect Oriented Programming (programmation par aspect) Implications : Enchevêtrement du code Faible réutilisabilité Qualité plus basse due à la complexité Difficulté à faire évoluer Préoccupations mélangées 38
SPRING AOP AOP : Aspect Oriented Programming (programmation par aspect) Solutions : Décomposer en aspect Programmer la partie métier Recomposer les aspects Préoccupations séparées Ajouter du comportement sans modification de code 39
SPRING AOP Intérêt de Spring Découplage des modules Maintenance accrue Meilleure réutilisation Gain de productivité Qualité du code améliorée Code non intrusif 40
SPRING AOP 2 types de programmations : Statique : au début ou à la fin Dynamique : au moment de l'exécution (plus lent) reconfiguration sans compilation 41
SPRING AOP Vocabulaire : Point de jonction (Joinpoint) Point d'actions (pointcut) Conseil / greffon (advice) Aspect Trammeur / tisseur (Weaver) 42
SPRING AOP Exemple : gestion des journaux package metier; public class Personne { // Affichage des informations de l'individu public String tostring2() { String s="nom=[" + this.nom + "], age=[" + this.age + "]"; System.out.println(s); return s; 43
SPRING AOP package test; import junit.framework.testcase; import org.springframework.context.applicationcontext; import org.springframework.context.support.classpathxmlapplicationcontext; import service.messageservice; public class PersonneTest extends TestCase { public void testpersonne() { ApplicationContext context = new ClassPathXmlApplicationContext( new String[] {"applicationcontext.xml" ); Personne p = (Personne) context.getbean("personne"); p.tostring2(); -> nom=[bob], age=[40] 44
SPRING AOP <?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <bean name="messageservice" class="service.messageservice" /> <!-- Debut de la configuration AOP --> <bean id="monlogger" class="aop.monlogger"/> <aop:config> <aop:pointcut id="servicepointcut" expression="execution(* service.messageservice.*(..))"/> <aop:aspect ref="monlogger"> <aop:before method="logdebutmethode" pointcut-ref="servicepointcut"/> <aop:after-returning method="logfinmethode" returning="result" pointcut-ref="servicepointcut"/> <aop:around method="injectaround" pointcut-ref="servicepointcut" /> </aop:aspect> </aop:config > <!-- Fin de la configuration AOP --> </beans> ApplicationContext.xml 45
SPRING AOP package aop; import org.aspectj.lang.joinpoint; import org.aspectj.lang.joinpoint.staticpart; public class MonLogger { // Cette méthode est appelée à chaque fois (et avant) qu'une méthode du package service //est interceptée public void logmethodentry(joinpoint joinpoint) { Object[] args = joinpoint.getargs(); // Nom de la méthode interceptée String name = joinpoint.getsignature().tolongstring(); StringBuffer sb = new StringBuffer(name + " called with: ["); // Liste des valeurs des arguments reçus par la méthode for(int i = 0; i < args.length; i++) { Object o = args[i]; sb.append("'"+o+"'"); sb.append((i == args.length - 1)? "" : ", "); sb.append("]"); System.out.println(sb); 46
SPRING AOP // suite // Cette méthode est appelée à chaque fois (et après) qu'une méthode du package service est // interceptée. Elle reçoit en argument 'result' qui est le retour de la méthode interceptée public void logmethodexit(staticpart staticpart, Object result) { // Nom de la méthode interceptée String name = staticpart.getsignature().tolongstring(); System.out.println(name + " returning: [" + result + "]"); // Cette méthode est appelée avant ET après qu'une méthode du package service est // interceptée. public Object injectaround(proceedingjoinpoint pjp) throws Throwable { System.out.println("INJECT BEFORE!"); // Execution de la méthode Object retval = null; try { retval = pjp.proceed(); catch (Exception e) { System.out.println("INJECT AFTER!"); return retval; 47
SPRING DATA ACCESS OBJECT 48
SPRING - DAO Design Pattern DAO : principe général 49
SPRING - DAO Design Pattern DAO : structure générale utilise encapsule Obtient Modifie Créé / utilise Création d'un objet DAO par classe métier (non par objet) 50
SPRING - DAO Principes pour Spring : Faire une abstraction par rapport à la méthode d accès à la base de données (framework de mapping, jdbc ) Offrir un système de management des transactions Offrir un système d accès aux données Offrir une hiérarchie d exceptions génériques. 51
SPRING - DAO Spring Invariable Spécifique - Ouvrir et fermer les connexions - Initialiser les ressources - Exécuter une requête - Récupérer le résultat Template Callback 52
SPRING - DAO 53
SPRING - DAO 54
SPRING - JDBCTemplate Intérêts : Définir les paramètres de connexion Ouvrir la connexion Spécifier les statements Préparer et exécuter les statements Établir la boucle permettant de parcourir les résultats (si il y en a) Faire le travail pour chaque itération Traiter n'importe quelle exception Se charger des transactions Fermer la connexion 55
SPRING - JDBCTemplate Configuration dans Spring <beans>... <bean id="derbydao" class="dao.derbydao"> <property name="datasource" ref="derbydatasource" /> </bean> <bean id="derbydatasource" class="org.springframework.jdbc.datasource.drivermanagerdatasource"> <property name="driverclassname"> <value>org.apache.derby.jdbc.clientdriver</value> </property> <property name="url"> <value>jdbc:derby://localhost:1527/location</value> </property> <property name="username"> <value>user</value> </property> <property name="password"> <value>mdp</value> </property> </bean> </beans> 56
SPRING - JDBCTemplate Exemple d'utilisation public class DerbyDAO { private JdbcTemplate jdbctemplate; public void setdatasource(datasource datasource) { this.jdbctemplate = new JdbcTemplate(dataSource); public int getcount() { return this.jdbctemplate.queryforint("select count(*) from location.personne"); //... public void insertpersonne(personne p) { this.jdbctemplate.update("insert into location.personne (nom,age) values (?,?)", new Object[]{p.getNom(),p.getAge()); 57
SPRING - JDBCTemplate //... suite public List getlist() { return getjdbctemplate().query("select * from location.personne", new BeanPropertyRowMapper(Personne.class)); 58
SPRING - Hibernate Rôles / intérêts : Gérer la persistance d'objets Java Offrir des outils de mapping Objet / Relationnel Lazy loading Abstraction de la couche JDBC (HQL...) 59
SPRING Hibernate Configuration complète dans Spring <bean id="datasource" class="org.apache.commons.dbcp.basicdatasource"> <property name="driverclassname"> <value>oracle.jdbc.driver.oracledriver</value> </property> <property name="url"> <value>jdbc:oracle:thin:@localhost:1521:test</value> </property> <property name="username"> <value>usr</value> </property> <property name="password"> <value>pwd</value> </property> </bean> <bean id="sessionfactorybean" class="org.springframework.orm.hibernate.localsessionfactorybean">... 60
SPRING Hibernate <property name="datasource"> <ref bean="datasource" /></property> <property name="hibernateproperties"> <props> <prop key="hibernate.dialect">net.sf.hibernate.dialect.oracledialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.cglib.use_reflection_optimizer">false</prop> </props> </property> <property name="mappingresources"> <list> <value>mapping/employee.hbm.xml</value> <value>mapping/codes.hbm.xml</value> </list> </property> </bean> <bean id="basicdatadao" class="impl.basicdatadaoimpl"> <property name="sessionfactory"> <ref bean="sessionfactorybean" /> </property> </bean> 61
SPRING Hibernate public class BasicDataDaoImpl extends HibernateDaoSupport implements IBasicDataDao { /** * Effacement d'un individu */ public void deleteindividu(string pindividuid) { // Recherche de l'existence de l'individu List l=gethibernatetemplate().find("from individu e where e.id='"+pindividuid+"'"); if(l.size()>0){ Individu ret=(individu)l.get(0); // Suppression gethibernatetemplate().delete(ret); 62
SPRING Conclusion : Très ouvert et ne limite pas les comportements : pour un même problème, il existe plusieurs solutions déroutant Spring facilite l'intégration et l'utilisation de librairies tierces, mais la connaissance et la compréhension de ces librairie restent nécessaires. Le comportement générique n'est pas forcément adapté aux besoins. Spring est structurant et améliore de façon significative la productivité et la maintenabilité des applications. Il n'y a pas là d'invention géniale mais plutôt un ensemble cohérent qui, bien que puissant, est simple et relativement intuitif à mettre en œuvre. 63
SPRING Bibliographie : http://www.springframework.org/ http://java.sun.com/blueprints/corej2eepatterns/patt erns/dataaccessobject.html Spring par la pratique, Eyrolles, 2006 Spring par l'exemple, PEARSSON, 2008 http://www.martinfowler.com/articles/injection.html 64