Date de première publication : 2019/10/21
Objectif et contexte
L'objectif de cet exercice est de mettre en oeuvre le "Hello World" de Spring Boot, puis de voir en action les servlets.
L'environnement de développement est le suivant :
- Java : Java SE 17 ou 21
- Gradle 8.5 ou gradlew
- SpringBoot 3 (3.2.1 en janvier 2024)
Consulter la version de java avec javac -v
.
Si cette version du compilateur Java est au moins 17, vous pouvez utiliser la version 3 du framework.
Dans le cas contraire, il faudra garder la version 2.7.
Découverte de SpringBoot
Mise en place des éléments
Cette partie est un extrait de la documentation officielle "serving web content"
Pour commencer, vous avez deux possibilités :
- se rendre sur la page de Spring Initializr : https://start.spring.io/
- OU BIEN créer un nouveau projet de type Spring Initializr sous IntelliJ par exemple. Les boites de dialogue remplacent l'interface web mais vous devez faire les mêmes choix.
On a sélectionné les options suivantes :
- Project : Gradle - groovy - le logiciel d'automatisation
- Langage : Java
- Spring Boot : 3.2.1 - la version stable la plus récente
- Dependencies
- Spring Boot DevTools
- Spring Web (pour faire une application Web)
- Project Metadata
- Group : app
- Artifact : bonjour
- Name : Bonjour
- Package name : app
- Java : 17
Vous obtenez alors l'arborescence suivante :
.
├── HELP.md
├── build.gradle
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── src
├── main
│ ├── java
│ │ └── app
│ │ └── BonjourApplication.java
│ └── resources
│ ├── application.properties
│ ├── static
│ └── templates
└── test
└── java
└── app
└── BonjourApplicationTests.java
Le couteau suisse à tout faire (compilation et déploiement) est Gradle et son wrapper (pour éviter l'installation) gradlew. Gradle téléchargera tous les éléments nécessaires au projet (sauf le JDK).
Fichier de configuration
Le fichier de configuration de notre application est build.gradle
:
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.1'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'app'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
tasks.named('test') {
useJUnitPlatform()
}
Bien entendu, il faut adapter le fichier en fonction de la version de java que l'on veut utiliser ou bien encore en fonction de la version du framework.
Suivant les besoins, il faudra le compléter (par exemple, pour utiliser le moteur de template à la fin du TP)
Répertoire src
src
est le répertoire source du projet. main
contient le code principal. java
le code écrit en java et app
est le package.
Dès la première construction du projet, vous trouverez un répertoire build
. Ce répertoire est regénéré à chaque build.
Page à afficher
Dans un premier temps, nous allons afficher une simple page web statique que vous devez placer dans le répertoire src/main/resources/static/index.html
(vous pouvez remplacer static
par public
si vous voulez ...)
<!DOCTYPE HTML>
<html>
<head>
<title>une premiere page web statique pour pring boot</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>
<body>
<p>Cette page statique est le point d'entree de l'application</p>
<p>Personnalisez avec votre PRENOM pour être sûr(e) que c'est bien votre page à VOUS !</p>
<!--
<p>un lien pour plus tard <a href="/bonjour">ici</a></p>-->
</body>
</html>
Mais il est où le serveur de page Ouaib ? Hein ? C'est l'application Spring Boot elle-même !
Toute la magie est dans la classe BonjourApplication
(la classe est trouvée automatiquement et il ne peut PAS y avoir deux classes avec l'annocation
@SpringBootApplication
package app;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class BonjourApplication {
public static void main(String[] args) {
SpringApplication.run(BonjourApplication.class, args);
}
}
Compilation et exécution
Pour voir le tout en action, il suffit de "compiler" le projet :
gradle build
Puis de lancer le serveur :
gradle bootRun
On peut aussi executer le fichier jar
qui se trouve dans le répertoire build/libs
:
java -jar build/libs/bonjour-0.1.0.jar
Ouvrez un navigateur et demandez l'ouverture de la page http://localhost:8080
ou bien utilisez curl
sur la même adresse.
Si vous avez une erreur de type "fallback", le serveur est bien fonctionnel mais vous avez oublié la page index.html
.
Le port 8080, standard, risque déjà d'être pris lors des TPs alors on peut en changer... Voici comment spécifier le port 9090, par exemple :
java -Dserver.port=9090 -jar build/libs/bonjour-0.1.0.jar
java -jar build/libs/bonjour-0.1.0.jar --server.port=9090
gradle bootRun --args='--server.port=9090'
Si vous spécifiez la valeur de port 0
, un port ouvert aléatoire sera choisi. On pourra le voir lors lancement de l'application (un Tomcat par défaut). Ce numéro de port peut aussi être récupéré par l'application elle-même.
La dernière possibilité consiste à préciser le numéro de port dans le fichier de configuration application.properties
dans src/main/resources
(la version en ligne de commande est prioritaire)
server.port=9090
Manipulation de servlet
Avec Java EE/Jakarta EE, le composant de base d'un conteneur de servlets est une ... servlet, c'est-à-dire une classe Java qui est compilée et exécutée pour rendre un "service".
Une première servlet
Voici le code d'une servlet de base :
package app;
import java.io.IOException;
import java.io.PrintWriter;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/prems/*", loadOnStartup = 1)
public class PremsServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException{
doGet(request,response);
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("Hello chers ZZ3!
");
}
}
À la lecture du code, vous aurez compris qu'elle est dans un fichier src/main/java/app
(vous pouvez également modifier le code pour faire un package spécifique app.servlets
- c'est une bonne pratique. Les annotations précisent respectivement à quelle URL la servlet va être exécutée et à quel moment elle est chargée (et donc compilée) en mémoire.
Les méthodes doGet()
et doPut()
sont appelées en fonction de la requête HTTP fournie. Dans l'exemple, l'une appelle l'autre pour avoir la même réponse.
Pour accéder à la servlet, il faut taper une URL du type
http://localhost:8080/prems
Si vous obtenez une erreur 404, vous pouvez vous être trompé de nom, mais il est aussi probable que vous ayez oublié l'annotation @ServletComponentScan
sur l'application SpringBoot
(le code de BonjourApplication
n'est pas tout à fait celui qui a été généré :-))
Enrichissement de la servlet
- Ajouter un compteur à la servlet en attribut, l'incrémenter et l'afficher lors de l'appel d'une requête. Rafraîchir plusieurs fois la page du navigateur !
- Vous pouvez afficher dans différents navigateurs, que constatez-vous ?
Si le compteur n'est pas incrémenté, redéployez l'application et vérifiez que la page n'est pas mise en cache. Quand ce compteur est-il remis à 0 ?
Interaction avec la servlet
Ajouter un formulaire contenant un champ texte et un bouton submit
par exemple dans le corps le fichier index.html
. L'attribut action
du formulaire fera appel à la servlet et l'attribut method
représentera la valeur POST
ou GET
(mode de passage des paramètres au serveur).
<form action="prems" method="get">
<input type="text" name="texte">
<input type="submit">
</form>
L'attribut name
(ou préférentiellemet id
) est nécessaire.
Modifier la servlet pour qu'elle affiche le champ texte du formulaire avec la méthode getParameter(nom)
de l'objet request
). Le paramètre nom
est la valeur de l'attribut name
ou de l'identifiant id
de l' élément du formulaire
On peut connaître le mode de passage des paramètres grâce à la méthode (getMethod()
de request
). La valeur est GET
ou POST
( et conforme à l'attribut method
de la balise form
Nous arrêtons là pour l'instant la découverte des servlets. Même si l'usage actuel consiste à les cacher, elles reviennent assez vite sur les choses qui sortent un peu du "facile et standard".
Vue et contrôleur
SpringBoot utilise un patron de conception MVC pour les applications (Spring MVC). Par la suite, on va s'intéresser uniquement à la vue : une page web et au contrôleur, un objet java que l'on appelle un bean...
Voici un exemple de contrôleur :
package app;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class BonjourController {
// le nom de la méthode n'est pas important, mais les annotations si !
@GetMapping("/bonjour")
public String disBonjour(@RequestParam(name="name", required=false, defaultValue="Moi") String name, Model model) {
model.addAttribute("name", name);
return "bonjour";
}
}
Le contrôleur est appelé lorsque l'URL mentionne /bonjour
avec la méthode GET
(on peut réagir aux différents verbes HTTP avec les noms d'annotations qui vont bien)
La méthode retourne le nom de la page web qui sera affichée par le contrôleur, soit bonjour
. Des informations pourront être passées à cette page au travers de l'instance de Model
. Dans le cas présent, on passe la valeur du paramètre "name" s'il est fourni ou une valeur par défaut sinon (par formulaire ou par modification d'URL)
http://localhost:8080/bonjour?name=moi
Il faut encore créer la page web qui sera affichée : soit statique, soit affichée au travers d'un template qui permet de faciliter l'écriture de la page (par rapport à l'utilisation d'une servlet par exemple)
Voici la page bonjour.html
du tutorial Spring :
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Premiere page avec template</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p>Le code est généré quand c'est nécessaire </p>
<p th:text="'Bonjour, ' + ${name} + '!'" />
</body>
</html>
Elle est à placer dans le répertoire src/main/resources/templates
. Le moteur de template utilisé est Thymeleaf Il faut modifier le fichier de build pour ce faire :
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
}
Si vous oubliez de faire cela, vous aurez une erreur de référence circulaire à la demande de la page ; erreur affichée à la fois par le navigateur et dans la console.
That's all for now folk !!