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 8, 11, 17 ou 19
- Gradle 7+ ou gradlew
- SpringBoot 2.7 ou 3
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.
Actuellement Java 19 et gradle ne sont pas compatibles.
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 Initilizr https://start.spring.io/ et de télécharger le fichier ZIP obtenu en ayant sélectionné Java, Gradle et Spring Web
- OU BIEN créer un nouveau projet de type Spring Initilizr sous IntelliJ par exemple. Les boites de dialogue remplacent l'interface web mais vous devez faire les mêmes choix.
Le fichier qui nous intéresse le plus est build.gradle
qui contient la configuration de notre projet et qui ressemble à quelque chose comme cela :
plugins {
id 'java'
id 'org.springframework.boot' version '2.7.7'
id 'io.spring.dependency-management' version '1.0.15.RELEASE'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
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 (3.0.1 en janvier 2023), ce qui donne :
plugins {
id 'java'
id 'org.springframework.boot' version '3.0.1'
id 'io.spring.dependency-management' version '1.1.0'
}
Ce qui est sympa avec un outil comme Gradle (et surtout Gradlew, le wrapper de Gradle sans install) et SpringBoot, c'est qu'il n'est pas nécessaire d'installer quoi que ce soit car tout est téléchargé au besoin (bon faut internet tout de même et un JDK)
Il est ensuite nécessaire de vérifier que l'aborescence suivante existe :
mkdir -p src/main/java/hello
mkdir -p src/main/resources/static
src
est le répertoire source du projet. main
contient le code principal. java
le code écrit en java et hello
est le package.
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>Getting Started: Serving Web Content</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>Get your greeting <a href="/greeting">here</a></p>-->
</body>
</html>
Mais il est où le serveur de page Ouaib ? Hein ? C'est l'application Spring Boot elle-même ! Il faut ajouter la classe src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@ServletComponentScan
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Compilation et exécution
Pour voir le tout en action, il suffit de "compiler" le projet et d'exécuter :
gradle build
gradle bootRun
On peut aussi executer le fichier jar
qui se trouve dans le répertoire build/libs
:
java -jar build/libs/gs-serving-web-content-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/gs-spring-boot-0.1.0.jar
java -jar build/libs/gs-spring-boot-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, 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 hello;
import java.io.IOException;
import java.io.PrintWriter;
// Pour la branche 2.7 de Spring Boot
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
// Pour la branche 3 de Spring Boot
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns = "/simple/*", loadOnStartup = 1)
public class SimpleServlet 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/hello
(vous pouvez également modifier le code pour faire un package spécifique hello.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/simple
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
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="simple" 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 hello;
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 GreetingController {
@GetMapping("/greeting")
public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
return "greeting";
}
}
Le contrôleur est appelé lorsque l'URL mentionne /greeting
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 greeting
. 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/greeting?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'un servlet par exemple)
Voici la page greeting.html
du tutorial Spring :
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Getting Started: Serving Web Content</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Hello, ' + ${name} + '!'" />
</body>
</html>
Elle est à placer dans le répertoire src/main/resources/templates
. Le moteur de template utilisé est Thymeleaf.
That's all for now folk !!