TP MVC
TP MVC
TP MVC
de Spring
1
Introduction
Le framework Spring1 est une boite `a outils tr`es riche permettant de structurer, dameliorer et de simplifier
lecriture dapplication JEE. Spring est organise en module
-
Pr
ealables
Preparez un environement pour tester une application WEB basee sur Spring-MVC :
Telechargez TomEE Plus3 (en local4 ) et decompressez larchive.
Preparez, dans Eclipse, une application WEB basee sur le conteneur TomEE (utilisez ladaptateur WTP pour
Tomcat en version 7).
Testez une page index.jsp tr`es simple.
<html>
<head><title>Example :: Spring Application</title></head>
<body>
<h1>Example - Spring Application</h1>
<p>This is my test.</p>
</body>
</html>
Verifiez que cette page est accessible. Faites quelques modifications mineures et testez le resultat. Parcourez
les traces de demarrage du serveur TomEE.
Preparez un onglet vers la documentation5 .
Preparez un onglet vers la Javadoc6 .
3.1
Un premier contr
oleur
Telechargez Spring 3.2.57 et ajoutez les archives .jar `a votre repertoire WEB-INF/lib. Faites de meme pour
les librairies qui se trouvent dans le repertoire libs-utiles-pour-TP-Spring-MVC8 .
1
http ://www.springframework.org/
http ://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/overview.html
3
http://tomee.apache.org/
4
ress-spring/
5
http ://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/index.html
6
http ://docs.spring.io/spring/docs/3.2.5.RELEASE/javadoc-api/index.html
7
ress-spring/
8
ress-spring/libs-utiles-pour-TP-Spring-MVC/
2
<servlet>
<servlet-name>springapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
Cette configuration va mettre en place une servlet generique qui va se charger du traitement des requetes (URL qui
se terminent par .htm). Pour configurer cette servlet, nous allons creer le fichier WEB-INF/springapp-servlet.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.spri
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<bean name="/hello.htm" class="springapp.web.HelloController" />
</beans>
Ce fichier de configuration Spring va creer un bean et lui donner un nom (/hello.htm). Cest notre premier
controleur. Creez cette classe `a partir du code ci-dessous :
package springapp.web;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import
import
import
import
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.Controller;
A cette etape, vous devez trouver les traces du serveur la creation de ce controleur.
Essayez dutiliser le controleur hello.htm
Visiblement ce controleur se termine en donnant la main `a une page JSP (hello.jsp) destinee `a fabriquer la
reponse `a envoyer au client. Creons cette page :
<html>
<head><title>Hello :: Spring Application</title></head>
<body>
<h1>Hello - Spring Application</h1>
</body>
</html>
Etudiez
la classe ModelAndView. Cette classe est le coeur du mod`ele MVC de Spring : Elle permet de separer
dune part, les controleur qui travaillent sur la requete et dautres part les vues (pages JSP) qui se chargent du
resultat. Entre les deux, les instances de ModelAndView transportent `a la fois le nom de la vue et les donnees
qui seront affiches par cette vue (cest-`a-dire le mod`
ele).
3.2
Am
eliorer les vues
Toutes les vues dune application partagent souvent de nombreuses declarations. Nous allons les regrouper dans
une page JSP. Preparez la page WEB-INF/jsp/include.jsp suivante :
<%@ page session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
Nous pouvons maintenant revoir notre page daccueil (fichier index.jsp)) en forcant lutilisateur `a utiliser le
controleur :
3
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.Controller;
Nous avons profite de cette mise `a jour pour introduire une nouveaut
e : le controleur fabrique maintenant une
donnee et transmet cette donnee `a la page JSP qui se charge de lafficher (la date).
Exercice : preparez une nouvelle donnee (par exemple un message), passez cette donnee `a la page hello.jsp
et assurez sa presentation.
4
3.3
D
ecoupler les vues et les contr
oleurs
Pour linstant, le chemin dacc`es complet `a la vue figure dans le controleur. Pour eviter ce couplage fort, nous
allons creez un service spring qui va se charger de retrouver les vues `a partir dun simple nom. Ajoutez au fichier
WEB-INF/springapp-servlet.xml le code :
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.JstlView"></property>
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.web.servlet.ModelAndView;
org.springframework.web.servlet.mvc.Controller;
Exercice : A ce stade, vous pouvez faire un classe de test unitaire pour verifier que votre controleur est bien
correct.
Pour linstant, nous avons fait du MVC Spring `a lancienne (interface, implantation et fichier de configuration
XML). Nous allons revoir ce processus en utilisant les annotations java.
Commencez par ajouter dans le fichier WEB-INF/springapp-servlet.xml la clause ci-dessous (avant la
creation du bean /hello.htm). Elle permet dindiquer `a la servlet quelle doit explorer les classes (des packages
listes) et exploiter les annotations.
Nous allons ensuite enrichier la correspondance URL/Controleurs en ajoutant la clause ci-dessous au fichier
WEBINF/web.xml :
<servlet-mapping>
<servlet-name>springapp</servlet-name>
<url-pattern>/actions/*</url-pattern>
</servlet-mapping>
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.stereotype.Controller;
org.springframework.web.bind.annotation.RequestMapping;
org.springframework.web.bind.annotation.RequestMethod;
org.springframework.web.servlet.ModelAndView;
@Controller()
@RequestMapping("/tests")
public class HelloAnnoController {
protected final Log logger = LogFactory.getLog(getClass());
@RequestMapping(value = "/welcome", method = RequestMethod.GET)
public ModelAndView sayHello() {
String now = (new Date()).toString();
logger.info("Running " + this);
return new ModelAndView("hello", "now", now);
}
}
Exercices :
Verifiez dans les traces du serveur que ces controleurs sont bien detectes par Spring.
Testez ce controleur avec une URL du type
http://localhost:8080/votre-application/actions/tests/welcome
4.1
D
eclarer les param`
etres
Nous pouvons egalement utiliser lannotation @RequestParam pour recuperer, sous la forme dun param`etre de
la methode, les param`etres de la requete HTTP. En voici un exemple :
@RequestMapping(value = "/plus10", method = RequestMethod.GET)
public ModelAndView plus10(
@RequestParam(value = "num", defaultValue = "100") Integer value) {
logger.info("Running plus10 controler with param = " + value);
return new ModelAndView("hello", "now", value + 10);
}
Exercices :
Testez ce controleur en lui fournissant le param`etre attendu. Testez egalement les cas derreur (param`etre
absent ou incorrect).
Ajoutez un nouveau param`etre de type Date et utilisez lannotation @DateTimeFormat pour recuperer ce
param`etre.
4.2
Il est maintenant habituel de placer des param`etres `a linterieur des adresses WEB. cela permet davoir des URL
simples, faciles `a construire et faciles `a memoriser. En voila un exemple :
@RequestMapping(value = "/voir/{param}", method = RequestMethod.GET)
public ModelAndView voir(@PathVariable("param") Integer param) {
logger.info("Running param controler with param=" + param);
return new ModelAndView("hello", "now", param);
}
Exercices :
Testez ce controleur.
Modifiez ce controleur pour avoir plusieurs param`etres dans la meme adresse.
Utilisez le mecanisme des expressions reguli`eres pour traiter une adresse composee (inspirez de la documention sur lannotation @PathVariable).
Terminez en testant lannotation @MatrixVariable pour traiter des URL de la forme (a et b ne sont pas
des param`etres) :
/une/action;a=10;b=20
Pour introduire la traitement des formulaires nous allons proceder en trois etapes :
definition dun mod`ele et dune couche metier,
creation du formulaire,
validation des donnees,
7
5.1
D
efinir une couche m
etier
Integer number;
String name;
Double price;
String description;
String type;
Nous allons lui adjoindre un service metier defini par linterface ci-dessous :
package springapp.business;
import java.util.Collection;
import springapp.model.Product;
public interface ProductManager {
Collection<Product> findAll();
void save(Product p);
Product find(int number);
}
package springapp.business;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.stereotype.Service;
import springapp.model.Product;
@Service("productManager")
public class InMemoryProductManager implements ProductManager {
final Map<Integer, Product> products;
int maxId = 0;
public InMemoryProductManager() {
this.products = new HashMap<Integer, Product>();
Product p1 = new Product();
p1.setNumber(100);
p1.setName("Car");
p1.setPrice(2000.0);
p1.setDescription("Small car");
products.put(p1.getNumber(), p1);
Product p2 = new Product();
p2.setNumber(200);
p2.setName("Gift");
p2.setPrice(100.0);
p2.setDescription("Big gift");
products.put(p2.getNumber(), p2);
maxId = 300;
}
@Override
public Collection<Product> findAll() {
return products.values();
}
@Override
public void save(Product p) {
if (p.getNumber() == null) {
p.setNumber(maxId++);
}
products.put(p.getNumber(), p);
}
@Override
public Product find(int number) {
Product p = products.get(number);
if (p == null) {
throw new IllegalArgumentException("no product " + number);
}
return p;
}
}
5.2
Nous pouvons maintenant mettre en place un controleur qui va gerer toutes les actions sur les produits (listage,
creation, modification et suppression).
10
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.beans.factory.annotation.Autowired;
org.springframework.stereotype.Controller;
org.springframework.web.bind.annotation.RequestMapping;
org.springframework.web.bind.annotation.RequestMethod;
org.springframework.web.servlet.ModelAndView;
import springapp.business.ProductManager;
import springapp.model.Product;
@Controller()
@RequestMapping("/product")
public class ProductController {
@Autowired
ProductManager manager;
protected final Log logger = LogFactory.getLog(getClass());
@RequestMapping(value = "/list", method = RequestMethod.GET)
public ModelAndView listProducts() {
logger.info("List of products");
Collection<Product> products = manager.findAll();
return new ModelAndView("productsList", "products", products);
}
}
11
Cette vue va construire la liste des produits, avec pour chacun une possibilite dedition. Bien entendu, pour
linstant, la phase dedition ne fonctionne pas.
Note : Dans cet exemple, le controleur ne fait rien dautre que de construire des donnees (la liste des produits) pour
les envoyer `a la vue. La creation des donnees peut etre decouplee des controleurs et placee dans des methodes
annotees par @ModelAttribute. Ces methodes sont systematiquement executees avant les controleurs pour
remplir le mod`ele.
Dans notre exemple, la creation de la liste des produits (nommee products) peut se faire par la methode :
@ModelAttribute("products")
Collection<Product> products() {
logger.info("Making list of products");
return manager.findAll();
}
Le controleur devient :
@RequestMapping(value = "/list", method = RequestMethod.GET)
public String listProducts2() {
logger.info("List of products");
return "productsList";
}
La vue va simplement puiser dans le mod`ele qui est rempli par la methode products.
5.3
Editer
un produit
12
Cette vue utilise les balises personnalisees de Spring pour gerer facilement la recuperation des donnees du mod`ele
(attribut commandName de la balise form) et la mise en place des champs (balises form :input, form :select,
etc...). Vous trouverez plus dinformation sur ces balises dans cette documentation9 .
Pour linstant, ce formulaire ne permet pas dediter des produits dej`a existants. Pour ce faire, nous allons ajouter
une methode annotee @ModelAttribute qui va preparer linstance du produit `a editer en fonction du param`etre
de la requete HTTP :
9
http://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/view.html#view-jsp-formtaglib
13
@ModelAttribute
public Product newProduct(
@RequestParam(value = "id", required = false) Integer productNumber) {
if (productNumber != null) {
logger.info("find product " + productNumber);
return manager.find(productNumber);
}
Product p = new Product();
p.setNumber(null);
p.setName("");
p.setPrice(0.0);
p.setDescription("");
logger.info("new product = " + p);
return p;
}
En clair : si la requete /edit est accompagnee dun numero de produit (param`etre id optionnel), le produit sera
charge `a partir du manager. Dans le cas contraire, un nouveau produit sera renvoye. Testez ce fonctionnement.
Il nous reste maintenant `a mettre en place le controleur de soumission du formulaire :
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String saveProduct(@ModelAttribute Product p, BindingResult result) {
if (result.hasErrors()) {
return "productForm";
}
manager.save(p);
return "productsList";
}
A cette etape, la seule erreur possible provient dune erreur de conversion sur le prix. Essayez de donner un prix
incorrect afin de tester ce fonctionnement.
Avertissement : A ce stade, la creation dun nouveau produit (apr`es la soumission) se termine sur laffichage
de la liste des produits (derni`ere ligne du controleur ci-dessus). Ce comportement pose un probl`eme : Si le client
tente un rechargement de la page, cela va provoquer une nouvelle soumission et la creation dun nouveau produit !
Pour regler ce probl`eme, nous allons renvoyer non pas sur la vue productsList, mais sur laction permettant
davoir la liste des produits :
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String saveProduct(@ModelAttribute Product p, BindingResult result) {
if (result.hasErrors()) {
return "productForm";
}
manager.save(p);
return "redirect:list";
}
5.4
Pour construire un formulaire complexe, nous avons souvent besoin dutiliser des donnees annexes (liste de
references, nom dutilisateur, etc.). Pour ce faire, nous allons de nouveau utiliser lannotation @ModelAttribute.
Mettez en place la methode suivante :
14
@ModelAttribute("productTypes")
public Map<String, String> productTypes() {
Map<String, String> types = new LinkedHashMap<>();
types.put("type1", "Type 1");
types.put("type2", "Type 2");
types.put("type3", "Type 3");
types.put("type4", "Type 4");
types.put("type5", "Type 5");
return types;
}
Elle fabrique et injecte dans le mod`ele une table de correspondance qui va nous etre utile pour ajouter le champ
de typage dans le formulaire. Modifiez notre formulaire en ajoutant :
<tr>
<td>Type : </td>
<td>
<form:select path="type" multiple="false">
<form:option value="" label="--- Select ---" />
<form:options items="${productTypes}" />
</form:select>
</td>
<td><form:errors path="type" cssClass="error" /></td>
</tr>
5.5
Il manque maintenant la phase de validation des donnees du formulaire. Pour ce faire, nous allons developper une
classe de validation adaptee au produit :
15
package springapp.web;
import
import
import
import
org.springframework.stereotype.Service;
org.springframework.validation.Errors;
org.springframework.validation.ValidationUtils;
org.springframework.validation.Validator;
import springapp.model.Product;
import springapp.model.ProductCode;
@Service
public class ProductValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Product.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Product product = (Product) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name",
"product.name", "Field name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "description",
"product.description", "Field description is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "type",
"product.type", "Field type is required.");
if (!(product.getPrice() > 0.0)) {
errors.rejectValue("price", "product.price.too.low",
"Price too low");
}
}
}
5.6
Pour linstant les messages derreurs sont affiches en anglais. Nous pouvons les traduire automatiquement en
delocalisant ces messages dans des fichiers de ressources.
Commencez par creer dans le repertoire contenant les sources du paquetage springapp.web le fichier product.properties
16
Nous pouvons simplifier notre classe de validation en supprimant les messages. Elle devient :
17
package springapp.web;
import
import
import
import
org.springframework.stereotype.Service;
org.springframework.validation.Errors;
org.springframework.validation.ValidationUtils;
org.springframework.validation.Validator;
import springapp.model.Product;
import springapp.model.ProductCode;
@Service
public class ProductValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Product.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Product product = (Product) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name",
"product.name");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "description",
"product.description");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "type",
"product.type");
if (!(product.getPrice() > 0.0)) {
errors.rejectValue("price", "product.price.too.low");
}
}
}
5.7
Nous venons de le voir, Spring MVC traite parfaitement les champs qui correspondent `a un type de base (entier,
flottant, chane, etc.). Nous allons maintenant nous occuper des champs complexes.
Commencons par definir une classe pour representer le numero de serie dun produit (une lettre suivie dun entier
entre 1000 et 9999) :
18
package springapp.model;
public class ProductCode {
String base;
int number;
public String getBase() {
return base;
}
public void setBase(String base) {
this.base = base;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
public ProductCode() {
super();
}
public ProductCode(String base, int number) {
super();
this.base = base;
this.number = number;
}
}
20
Il suffit maintenant dindiquer au controleur que nous disposons de cette classe. Pour ce faire nous allons lui
adjoindre une methode annotee par InitBinder :
@InitBinder
public void initBinder(WebDataBinder b) {
b.registerCustomEditor(ProductCode.class, new ProductCodeEditor());
}
Une nouvelle specification (JSR303) nous permet dexprimer les contraintes sur les proprietes par des annotations
(plus dinformation dans la documentation JEE 610 et dans la JavaDoc11 ).
6.1
Mise en oeuvre
http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html
http ://docs.oracle.com/javaee/6/api/javax/validation/constraints/package-summary.html
21
package springapp.model;
import
import
import
import
javax.validation.Valid;
javax.validation.constraints.Min;
javax.validation.constraints.NotNull;
javax.validation.constraints.Size;
et
package springapp.model;
import
import
import
import
javax.validation.constraints.Max;
javax.validation.constraints.Min;
javax.validation.constraints.NotNull;
javax.validation.constraints.Size;
Nous allons indiquer `a Spring que nous utilisons la validation par lutilisation des annotations (fichier WEB-INF/springapp-s
22
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
....
<!-- support JSR303 annotation if JSR 303 validation present on classpath -->
<mvc:annotation-driven />
....
</beans>
Testez le resultat. Vous devez vous retrouver avec les messages en provenance du validateur plus les nouveaux
messages en provenance des annotations. Vous pouvez maintenant vider votre validateur manuel. La classe de
validation reste utile pour les validations m
etier.
6.2
Cr
eer ses propres contraintes
Le mecanisme de validation peut facilement etre etendu pas ajout de contraintes specifiques. Nous allons creer
une contrainte Bye qui va verifier la presence de ce mot dans un champ. Pour ce faire, commencez par creez
lannotation Bye :
23
package springapp.web;
import
import
import
import
import
java.lang.annotation.Documented;
java.lang.annotation.ElementType;
java.lang.annotation.Retention;
java.lang.annotation.RetentionPolicy;
java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = ByeConstraintValidator.class)
@Documented
public @interface Bye {
String message() default "Il manque le bye";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
24
Il est facile de recuperer des donnees placees en session, mais Spring nous offre le moyen dinjecter directement
dans nos controleurs des donnees de portee session.
Etape
1 : definissez un nouveau bean pour representer lutilisateur courant :
package springapp.web;
import java.io.Serializable;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;
@Component()
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Lannotation Component indique que cest un composant gere par Spring. Lannotation Scope donne la portee
des instances (une par session). La clause ProxyMode permet dindiquer que ce nest pas directement une instance
qui doit etre injectee, mais un proxy qui va selectionner la bonne instance (dans la bonne session) en fonction du
contexte.
Etape
2 : definissez un controleur qui utilise linjection du bean User :
25
package springapp.web;
import
import
import
import
import
import
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.beans.factory.annotation.Autowired;
org.springframework.stereotype.Controller;
org.springframework.web.bind.annotation.ModelAttribute;
org.springframework.web.bind.annotation.RequestMapping;
@Controller()
@RequestMapping("/user")
public class UserController {
protected final Log logger = LogFactory.getLog(getClass());
@Autowired()
User user;
@ModelAttribute("user")
public User newUser() {
return user;
}
@RequestMapping(value = "/show")
public String show() {
logger.info("show user " + user);
return "user";
}
@RequestMapping(value = "/login")
public String login() {
logger.info("login user " + user);
user.setName("Its me");
return "user";
}
@RequestMapping(value = "/logout")
public String logout() {
logger.info("logout user " + user);
user.setName("Anonymous");
return "user";
}
}
Etape
3 : La vue :
26
Moralit
e : Le controleur (qui est un singleton execute par plusieurs threads) utilise le proxy pour selectionner
automatiquement linstance du bean User qui correspond `a la requete courante et `a la session courante.
La liaison se fait par le thread. Cest le meme thread qui traite toute la requete (Dispatcher, controleur,
vue). Le thread courant est donc utilise comme une sorte de variable globale qui permet de faire des liaisons
implicites.
Nous allons maintenant etudier comment mettre en place une couche metier basee sur des EJBs.
8.1
D
efinition des EJBs
27
package springapp.services;
import
import
import
import
import
javax.persistence.Column;
javax.persistence.Entity;
javax.persistence.GeneratedValue;
javax.persistence.Id;
javax.persistence.Table;
@Entity
@Table(name = "WA_MESSAGE")
public class Message {
@Id
@GeneratedValue
Integer number;
@Column
String text;
public Message() {
super();
}
public Message(String text) {
super();
this.text = text;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
28
javax.annotation.PostConstruct;
javax.ejb.LocalBean;
javax.ejb.Startup;
javax.ejb.Stateless;
javax.persistence.EntityManager;
javax.persistence.PersistenceContext;
@Stateless
@LocalBean()
@Startup
public class MessageManager implements IMessageManager {
@PersistenceContext(unitName = "myData")
EntityManager em;
public MessageManager() {
}
@PostConstruct
public void init() {
System.out.println("INIT EJB = " + this);
}
@Override
public void add(String data) {
Message m = new Message(data);
em.persist(m);
}
@Override
public int removeAll() {
return em.createQuery("Delete From Message").executeUpdate();
}
@Override
public Collection<Message> findAll() {
return em.createQuery("Select m From Message m", Message.class)
.getResultList();
}
}
29
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
Dans cette version nous utilisons le fournisseur de persistance par defaut de TomEE : OpenJPA12 . Nous utilisons
egalement la DataSource par defaut de TomEE (basee sur HSql).
8.2
Configurer Spring
Vous devez ajouter la creation de lEJB dans le fichier XML de configuration de votre application :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee.xsd
">
...
<jee:jndi-lookup id="messageManager"
jndi-name="java:global/votre-application/MessageManager" />
...
</beans>
8.3
La partie WEB
http://openjpa.apache.org/
http
://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/xsd-config.html#xsd-config-bodyschemas-jee
13
30
package springapp.web;
import java.util.Collection;
import
import
import
import
import
import
import
import
org.apache.commons.logging.Log;
org.apache.commons.logging.LogFactory;
org.springframework.beans.factory.annotation.Autowired;
org.springframework.stereotype.Controller;
org.springframework.web.bind.annotation.ModelAttribute;
org.springframework.web.bind.annotation.RequestMapping;
org.springframework.web.bind.annotation.RequestParam;
org.springframework.web.servlet.ModelAndView;
import springapp.services.IMessageManager;
import springapp.services.Message;
@Controller()
@RequestMapping("/message")
public class MessageController {
@Autowired
IMessageManager messageManger;
protected final Log logger = LogFactory.getLog(getClass());
@RequestMapping(value = "/add")
public ModelAndView add(@RequestParam(required = true) String text) {
messageManger.add(text);
return new ModelAndView("message", "messages", messages());
}
@RequestMapping(value = "/removeAll")
public ModelAndView removeAll() {
int n = messageManger.removeAll();
logger.info(n + " deleted message(s)");
return new ModelAndView("message", "messages", messages());
}
@RequestMapping(value = "/list")
public String list() {
return "message";
}
@ModelAttribute("messages")
public Collection<Message> messages() {
return messageManger.findAll();
}
}
31
Cest fini
` bientot.
A
14
http ://docs.spring.io/spring/docs/3.2.5.RELEASE/spring-framework-reference/html/orm.html#orm-jpa
32