Apprendre Spring Boot avec Java

📘 Sommaire

  1. Introduction Ă  Spring Boot

    • Qu’est-ce que Spring Boot ?
    • DiffĂ©rences entre Spring Framework et Spring Boot
    • Avantages de Spring Boot : simplicitĂ©, rapiditĂ©, convention over configuration
    • Installation de l’environnement de dĂ©veloppement (JDK, IDE, Maven/Gradle)
    • CrĂ©ation d’un projet Spring Boot avec Spring Initializr
  2. Architecture d’une application Spring Boot

    • Structure d’un projet Spring Boot
    • Les annotations principales : @SpringBootApplication, @RestController, @Service, @Repository
    • Le rĂŽle de la classe principale avec public static void main
    • Configuration de l’application via application.properties ou application.yml
  3. Injection de dépendances et gestion des beans

    • Principe de l’inversion de contrĂŽle (IoC)
    • Types d’injection : par constructeur, par setter, par champ
    • Cycle de vie des beans : initialisation, destruction
    • Profils Spring : gestion des environnements (dev, prod, test)
  4. DĂ©veloppement d’une application Web avec Spring MVC

    • Architecture MVC : ModĂšle, Vue, ContrĂŽleur
    • CrĂ©ation de contrĂŽleurs avec @GetMapping, @PostMapping, etc.
    • Gestion des vues avec Thymeleaf et Bootstrap
    • Validation des formulaires avec @Valid et BindingResult
    • Gestion des erreurs et des exceptions
  5. AccÚs aux données avec Spring Data JPA

    • Introduction Ă  JPA et Hibernate
    • CrĂ©ation d’entitĂ©s avec @Entity, @Id, @GeneratedValue
    • DĂ©finition de repositories avec JpaRepository
    • RequĂȘtes personnalisĂ©es avec @Query et Query Methods
    • Gestion des transactions avec @Transactional
  6. DĂ©veloppement d’API RESTful

    • Principes REST : stateless, ressources, verbes HTTP
    • CrĂ©ation de services REST avec @RestController
    • SĂ©rialisation et dĂ©sĂ©rialisation JSON avec Jackson
    • Gestion des codes de statut HTTP et des rĂ©ponses personnalisĂ©es
    • SĂ©curisation des API avec Spring Security et JWT
  7. Tests avec Spring Boot

    • Tests unitaires avec JUnit 5 et Mockito
    • Tests d’intĂ©gration avec @SpringBootTest
    • Tests de contrĂŽleurs avec @WebMvcTest
    • Tests de services avec @MockBean
    • Utilisation de Postman pour tester les API
  8. SĂ©curisation d’une application Spring Boot

    • Introduction Ă  Spring Security
    • Authentification : en mĂ©moire, en base de donnĂ©es, via LDAP
    • Autorisation : rĂŽles et permissions
    • SĂ©curisation des API REST avec JWT
    • Gestion des sessions et des cookies
  9. DĂ©ploiement d’une application Spring Boot

    • CrĂ©ation d’un fichier exĂ©cutable .jar ou .war
    • DĂ©ploiement sur un serveur Tomcat ou Jetty
    • DĂ©ploiement sur des plateformes cloud : Heroku, AWS, Azure
    • CrĂ©ation d’une image Docker pour l’application
    • DĂ©ploiement avec Kubernetes
  10. Bonnes pratiques et outils complémentaires

    • Utilisation de Spring Boot DevTools pour le redĂ©marrage automatique
    • Surveillance de l’application avec Spring Boot Actuator
    • Gestion des logs avec SLF4J et Logback
    • Documentation de l’API avec Swagger/OpenAPI
    • Gestion des dĂ©pendances avec Maven ou Gradle

1. Introduction Ă  Spring Boot

Qu’est-ce que Spring Boot ?

Spring Boot est un projet du Spring Framework qui vise Ă  simplifier la crĂ©ation d’applications Spring autonomes, prĂȘtes Ă  l’emploi (“production-ready”). Il met l’accent sur la “convention over configuration”, rĂ©duisant ainsi la quantitĂ© de configuration boilerplate nĂ©cessaire pour dĂ©marrer un projet Spring.

Les objectifs principaux de Spring Boot sont :

  • Fournir une expĂ©rience de dĂ©veloppement Spring plus rapide et largement accessible.
  • RĂ©duire la configuration XML et Groovy.
  • Offrir un ensemble de fonctionnalitĂ©s non fonctionnelles communes (comme les mĂ©triques, les contrĂŽles de santĂ© et la configuration externalisĂ©e) prĂȘtes Ă  l’emploi.
  • Éviter la nĂ©cessitĂ© d’une configuration XML complexe.

Différences entre Spring Framework et Spring Boot

Spring Framework est un framework d’application Java complet qui fournit une infrastructure pour le dĂ©veloppement d’applications d’entreprise. Il offre une large gamme de fonctionnalitĂ©s, notamment l’injection de dĂ©pendances, la gestion transactionnelle, l’accĂšs aux donnĂ©es, le dĂ©veloppement web (Spring MVC), etc. Cependant, la configuration initiale d’un projet Spring Framework peut ĂȘtre complexe et fastidieuse, nĂ©cessitant souvent beaucoup de configuration XML ou Java.

Spring Boot s’appuie sur Spring Framework mais simplifie considĂ©rablement le processus de dĂ©marrage et de configuration. Il offre des configurations automatiques basĂ©es sur les dĂ©pendances prĂ©sentes dans le classpath, des serveurs embarquĂ©s (Tomcat, Jetty, Undertow), et une approche opinionated pour le dĂ©veloppement d’applications Spring. En bref, Spring Boot rend le dĂ©veloppement avec Spring plus rapide et plus facile.

Avantages de Spring Boot : simplicité, rapidité, convention over configuration

  • SimplicitĂ© : Spring Boot simplifie grandement la configuration des applications Spring grĂące Ă  l’auto-configuration et aux starters.
  • RapiditĂ© : Le dĂ©marrage rapide des applications et la rĂ©duction de la configuration permettent de dĂ©velopper et de dĂ©ployer des applications plus rapidement.
  • Convention over Configuration : Spring Boot favorise les conventions par dĂ©faut, ce qui rĂ©duit la nĂ©cessitĂ© de configurer explicitement de nombreux aspects de l’application. Cela permet aux dĂ©veloppeurs de se concentrer sur la logique mĂ©tier.
  • Serveurs embarquĂ©s : Il inclut des serveurs web embarquĂ©s (Tomcat, Jetty, Undertow), ce qui permet de crĂ©er des applications autonomes qui peuvent ĂȘtre exĂ©cutĂ©es directement avec un simple java -jar.
  • Starters : Les “Starters” sont des ensembles de dĂ©pendances prĂ©configurĂ©es qui facilitent l’ajout de fonctionnalitĂ©s Ă  votre application (par exemple, spring-boot-starter-web pour le dĂ©veloppement web, spring-boot-starter-data-jpa pour l’accĂšs aux donnĂ©es).

Installation de l’environnement de dĂ©veloppement (JDK, IDE, Maven/Gradle)

Pour développer avec Spring Boot, vous aurez besoin des éléments suivants :

  1. JDK (Java Development Kit) : Spring Boot nĂ©cessite Java 8 ou une version ultĂ©rieure. Vous pouvez tĂ©lĂ©charger le JDK depuis le site d’Oracle ou utiliser une distribution OpenJDK.
  2. IDE (Integrated Development Environment) : Un IDE facilite grandement le développement. Les IDE populaires pour le développement Spring Boot incluent :
    • Spring Tool Suite (STS) : BasĂ© sur Eclipse, spĂ©cifiquement conçu pour le dĂ©veloppement Spring.
    • IntelliJ IDEA : Une option trĂšs populaire avec une excellente prise en charge de Spring.
    • VS Code : Avec les extensions Java et Spring Boot appropriĂ©es.
  3. Outil de build : Vous aurez besoin d’un outil pour gĂ©rer les dĂ©pendances et construire votre projet. Les plus couramment utilisĂ©s sont :
    • Maven : Un outil de gestion de projet basĂ© sur le concept de Project Object Model (POM).
    • Gradle : Un outil de build flexible et performant.

Assurez-vous que le JDK est installĂ© et que les variables d’environnement JAVA_HOME et PATH sont correctement configurĂ©es. Installez votre IDE prĂ©fĂ©rĂ© et l’outil de build de votre choix.

CrĂ©ation d’un projet Spring Boot avec Spring Initializr

Spring Initializr est un service web qui permet de gĂ©nĂ©rer rapidement la structure de base d’un projet Spring Boot. C’est le moyen le plus simple et le plus recommandĂ© pour dĂ©marrer un nouveau projet.

Étapes pour crĂ©er un projet avec Spring Initializr :

  1. Ouvrez votre navigateur et allez sur https://start.spring.io/.
  2. Configurez les options de votre projet :
    • Project : Choisissez Maven Project ou Gradle Project.
    • Language : Choisissez Java.
    • Spring Boot : SĂ©lectionnez la version de Spring Boot (il est recommandĂ© d’utiliser la derniĂšre version stable).
    • Project Metadata : Remplissez les informations du projet (Group, Artifact, Name, Description, Package name).
    • Packaging : Choisissez Jar (pour une application autonome avec serveur embarquĂ©) ou War (pour dĂ©ployer sur un serveur d’applications externe).
    • Java : SĂ©lectionnez la version de Java que vous souhaitez utiliser.
    • Dependencies : Ajoutez les dĂ©pendances nĂ©cessaires pour votre application. Par exemple, pour une application web, ajoutez “Spring Web”. Pour l’accĂšs aux donnĂ©es, ajoutez “Spring Data JPA” et le driver de base de donnĂ©es appropriĂ© (par exemple, “H2 Database” pour une base de donnĂ©es en mĂ©moire).
  3. Cliquez sur le bouton “Generate”. Cela tĂ©lĂ©chargera un fichier ZIP contenant la structure de base de votre projet.
  4. Extrayz le fichier ZIP et importez le projet dans votre IDE.

Votre projet Spring Boot est maintenant prĂȘt Ă  ĂȘtre dĂ©veloppĂ© !


2. Architecture d’une application Spring Boot

Structure d’un projet Spring Boot

Un projet Spring Boot gĂ©nĂ©rĂ© par Spring Initializr a gĂ©nĂ©ralement une structure de rĂ©pertoire standard qui facilite l’organisation du code. Voici une structure typique :

myproject/
├── pom.xml (ou build.gradle)
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/myproject/
│   │   │       ├── MyprojectApplication.java  // Classe principale
│   │   │       ├── controller/             // Contrîleurs REST ou MVC
│   │   │       ├── service/                // Services mĂ©tier
│   │   │       ├── repository/             // Repositories pour l'accĂšs aux donnĂ©es
│   │   │       └── model/                  // Classes de modĂšle (entitĂ©s JPA, DTOs)
│   │   └── resources/
│   │       ├── application.properties (ou application.yml) // Fichier de configuration
│   │       ├── static/                     // Fichiers statiques (CSS, JS, images)
│   │       └── templates/                  // Templates de vues (Thymeleaf, FreeMarker)
│   └── test/
│       └── main/
│           └── java/
│               └── com/example/myproject/
│                   └── MyprojectApplicationTests.java // Classe de test
└── target/ (ou build/)
    └── myproject.jar (ou .war) // Fichier exĂ©cutable ou dĂ©ployable
  • pom.xml (Maven) ou build.gradle (Gradle) : Fichier de configuration du build, listant les dĂ©pendances et les plugins.
  • src/main/java : Contient le code source Java principal de l’application.
  • src/main/resources : Contient les fichiers de configuration, les fichiers statiques et les templates de vues.
  • src/test/java : Contient le code source des tests.
  • target/ (Maven) ou build/ (Gradle) : Contient les fichiers gĂ©nĂ©rĂ©s par le build (classes compilĂ©es, JAR/WAR).

Les annotations principales : @SpringBootApplication, @RestController, @Service, @Repository

Spring Boot utilise intensivement les annotations pour configurer et dĂ©finir les composants de l’application. Voici quelques-unes des annotations les plus courantes :

  • @SpringBootApplication: Cette annotation est une annotation de commoditĂ© qui combine @Configuration, @EnableAutoConfiguration et @ComponentScan. Elle est gĂ©nĂ©ralement placĂ©e sur la classe principale de l’application et active l’auto-configuration de Spring Boot et la recherche de composants dans le package courant et ses sous-packages.

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
     
    @SpringBootApplication
    public class MyprojectApplication {
        public static void main(String[] args) {
            SpringApplication.run(MyprojectApplication.class, args);
        }
    }
  • @RestController: Une annotation spĂ©cialisĂ©e pour les contrĂŽleurs RESTful. Elle combine @Controller et @ResponseBody, ce qui signifie que les mĂ©thodes de ce contrĂŽleur retournent directement des donnĂ©es (gĂ©nĂ©ralement au format JSON ou XML) plutĂŽt que des noms de vues.

    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
     
    @RestController
    public class MyController {
     
        @GetMapping("/hello")
        public String sayHello() {
            return "Hello, Spring Boot!";
        }
    }
  • @Service: Indique qu’une classe est un composant de service, gĂ©nĂ©ralement utilisĂ© pour encapsuler la logique mĂ©tier. Spring gĂšre le cycle de vie de ces beans.

    import org.springframework.stereotype.Service;
     
    @Service
    public class MyService {
        public String processData() {
            return "Processed data";
        }
    }
  • @Repository: Indique qu’une classe est un composant de repository, gĂ©nĂ©ralement utilisĂ© pour l’accĂšs aux donnĂ©es (interactions avec la base de donnĂ©es). Spring fournit des fonctionnalitĂ©s supplĂ©mentaires pour les repositories, comme la traduction des exceptions spĂ©cifiques aux bases de donnĂ©es en exceptions Spring DataAccessException.

    import org.springframework.stereotype.Repository;
     
    @Repository
    public class MyRepository {
        // Méthodes d'accÚs aux données
    }

Le rĂŽle de la classe principale avec public static void main

La classe principale d’une application Spring Boot est le point d’entrĂ©e de l’application. Elle contient la mĂ©thode main standard de Java, annotĂ©e avec @SpringBootApplication.

La méthode SpringApplication.run():

  • DĂ©marre le conteneur Spring.
  • Effectue l’auto-configuration.
  • Lance le serveur web embarquĂ© (si applicable).

C’est cette mĂ©thode qui initialise et exĂ©cute l’application Spring Boot.

Configuration de l’application via application.properties ou application.yml

Spring Boot permet de configurer facilement votre application Ă  l’aide de fichiers de propriĂ©tĂ©s. Les formats les plus courants sont application.properties et application.yml (YAML). Ces fichiers sont gĂ©nĂ©ralement placĂ©s dans le rĂ©pertoire src/main/resources.

Exemple dans application.properties:

server.port=8080
spring.datasource.url=jdbc:h2:mem:mydb
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true

Exemple dans application.yml:

server:
  port: 8080
spring:
  datasource:
    url: jdbc:h2:mem:mydb
    username: sa
    password:
  h2:
    console:
      enabled: true

Ces fichiers permettent de configurer divers aspects de l’application, tels que le port du serveur, la configuration de la base de donnĂ©es, les paramĂštres de logging, etc. Spring Boot charge automatiquement ces configurations au dĂ©marrage.


3. Injection de dépendances et gestion des beans

Principe de l’inversion de contrîle (IoC)

L’Inversion de ContrĂŽle (IoC) est un principe de conception logicielle dans lequel le contrĂŽle de l’objet ou de la fonction est transfĂ©rĂ© Ă  un conteneur ou un framework. Dans le contexte de Spring, le conteneur IoC (reprĂ©sentĂ© par l’interface ApplicationContext) est responsable de l’instanciation, de la configuration et de l’assemblage des beans (objets gĂ©rĂ©s par Spring).

Au lieu que les objets crĂ©ent ou recherchent leurs dĂ©pendances, le conteneur IoC injecte les dĂ©pendances dans les objets. Cela rĂ©duit le couplage entre les composants et rend l’application plus modulaire et plus facile Ă  tester.

Types d’injection : par constructeur, par setter, par champ

Spring prend en charge plusieurs types d’injection de dĂ©pendances :

  • Injection par constructeur : Les dĂ©pendances sont fournies via les arguments du constructeur de la classe. C’est le type d’injection recommandĂ© car il garantit que les dĂ©pendances requises sont prĂ©sentes lors de la crĂ©ation de l’objet et facilite les tests unitaires.

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
     
    @Service
    public class MyService {
     
        private final MyRepository myRepository;
     
        @Autowired
        public MyService(MyRepository myRepository) {
            this.myRepository = myRepository;
        }
     
        // ...
    }
  • Injection par setter : Les dĂ©pendances sont injectĂ©es via les mĂ©thodes setter de la classe. Cela rend les dĂ©pendances optionnelles et permet de modifier les dĂ©pendances aprĂšs la crĂ©ation de l’objet.

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
     
    @Service
    public class MyService {
     
        private MyRepository myRepository;
     
        @Autowired
        public void setMyRepository(MyRepository myRepository) {
            this.myRepository = myRepository;
        }
     
        // ...
    }
  • Injection par champ : Les dĂ©pendances sont injectĂ©es directement dans les champs de la classe Ă  l’aide de l’annotation @Autowired. Bien que plus concise, cette mĂ©thode est gĂ©nĂ©ralement dĂ©conseillĂ©e car elle rend la classe plus difficile Ă  tester (les dĂ©pendances sont cachĂ©es) et viole le principe de l’encapsulation.

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
     
    @Service
    public class MyService {
     
        @Autowired
        private MyRepository myRepository;
     
        // ...
    }

Cycle de vie des beans : initialisation, destruction

Les beans gĂ©rĂ©s par le conteneur Spring ont un cycle de vie qui comprend plusieurs Ă©tapes, notamment l’initialisation et la destruction. Vous pouvez dĂ©finir des mĂ©thodes de callback pour exĂ©cuter du code Ă  ces Ă©tapes.

  • Initialisation : Des mĂ©thodes peuvent ĂȘtre appelĂ©es aprĂšs que le bean a Ă©tĂ© instanciĂ© et que ses dĂ©pendances ont Ă©tĂ© injectĂ©es. Vous pouvez utiliser :

    • L’annotation @PostConstruct (JSR-250).
    • L’interface InitializingBean (mĂ©thode afterPropertiesSet()).
    • Une mĂ©thode spĂ©cifiĂ©e dans la configuration du bean (par exemple, l’attribut init-method en XML).
    import javax.annotation.PostConstruct;
    import org.springframework.stereotype.Component;
     
    @Component
    public class MyBean {
     
        @PostConstruct
        public void init() {
            // Code exécuté aprÚs l'initialisation du bean
            System.out.println("MyBean initialized");
        }
     
        // ...
    }
  • Destruction : Des mĂ©thodes peuvent ĂȘtre appelĂ©es avant que le bean ne soit retirĂ© du conteneur. Vous pouvez utiliser :

    • L’annotation @PreDestroy (JSR-250).
    • L’interface DisposableBean (mĂ©thode destroy()).
    • Une mĂ©thode spĂ©cifiĂ©e dans la configuration du bean (par exemple, l’attribut destroy-method en XML).
    import javax.annotation.PreDestroy;
    import org.springframework.stereotype.Component;
     
    @Component
    public class MyBean {
     
        // ...
     
        @PreDestroy
        public void cleanup() {
            // Code exécuté avant la destruction du bean
            System.out.println("MyBean destroyed");
        }
    }

Profils Spring : gestion des environnements (dev, prod, test)

Les profils Spring permettent de gĂ©rer des configurations spĂ©cifiques Ă  diffĂ©rents environnements (dĂ©veloppement, production, test, etc.). Vous pouvez marquer des beans ou des configurations avec l’annotation @Profile pour indiquer qu’ils ne doivent ĂȘtre activĂ©s que lorsque le profil correspondant est actif.

Exemple :

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
 
@Configuration
@Profile("dev")
public class DevConfig {
 
    @Bean
    public MyService devService() {
        return new MyService("Development Service");
    }
}
 
@Configuration
@Profile("prod")
public class ProdConfig {
 
    @Bean
    public MyService prodService() {
        return new MyService("Production Service");
    }
}

Vous pouvez activer un profil en utilisant la propriĂ©tĂ© spring.profiles.active dans application.properties ou application.yml, ou via une variable d’environnement ou un argument de ligne de commande.

Exemple dans application.properties:

spring.profiles.active=dev

4. DĂ©veloppement d’une application Web avec Spring MVC

Architecture MVC : ModĂšle, Vue, ContrĂŽleur

Spring MVC suit le modĂšle de conception Model-View-Controller (MVC), qui sĂ©pare l’application en trois composants principaux :

  • ModĂšle (Model) : ReprĂ©sente les donnĂ©es de l’application et la logique mĂ©tier. Il est indĂ©pendant de l’interface utilisateur.
  • Vue (View) : Est responsable de l’affichage des donnĂ©es du modĂšle Ă  l’utilisateur. Elle peut ĂȘtre implĂ©mentĂ©e Ă  l’aide de technologies comme Thymeleaf, JSP, etc.
  • ContrĂŽleur (Controller) : Agit comme un intermĂ©diaire entre le ModĂšle et la Vue. Il reçoit les requĂȘtes de l’utilisateur, interagit avec le ModĂšle pour rĂ©cupĂ©rer ou mettre Ă  jour les donnĂ©es, et sĂ©lectionne la Vue appropriĂ©e pour afficher la rĂ©ponse.

Spring MVC fournit les outils nĂ©cessaires pour implĂ©menter cette architecture, notamment le DispatcherServlet qui agit comme le contrĂŽleur frontal, acheminant les requĂȘtes vers les contrĂŽleurs appropriĂ©s.

Création de contrÎleurs avec @GetMapping, @PostMapping, etc.

Dans Spring MVC, les contrĂŽleurs sont des classes annotĂ©es avec @Controller ou @RestController. Les mĂ©thodes de ces contrĂŽleurs gĂšrent les requĂȘtes HTTP entrantes et retournent une rĂ©ponse.

Les annotations de mapping les plus courantes sont :

  • @RequestMapping: Annotation gĂ©nĂ©rale pour mapper les requĂȘtes web aux mĂ©thodes du contrĂŽleur.
  • @GetMapping: Raccourci pour @RequestMapping(method = RequestMethod.GET).
  • @PostMapping: Raccourci pour @RequestMapping(method = RequestMethod.POST).
  • @PutMapping: Raccourci pour @RequestMapping(method = RequestMethod.PUT).
  • @DeleteMapping: Raccourci pour @RequestMapping(method = RequestMethod.DELETE).
  • @PatchMapping: Raccourci pour @RequestMapping(method = RequestMethod.PATCH).

Exemple de contrĂŽleur simple :

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 WebController {
 
    @GetMapping("/greeting")
    public String greeting(@RequestParam(name="name", required=false, defaultValue="World") String name, Model model) {
        model.addAttribute("name", name);
        return "greeting"; // Retourne le nom de la vue (template)
    }
}

Dans cet exemple, la mĂ©thode greeting gĂšre les requĂȘtes GET sur /greeting, prend un paramĂštre de requĂȘte name, ajoute un attribut au modĂšle et retourne le nom de la vue “greeting”.

Gestion des vues avec Thymeleaf et Bootstrap

Spring Boot prend en charge plusieurs technologies de vues. Thymeleaf est un moteur de templates cĂŽtĂ© serveur populaire pour les applications web Java. Il s’intĂšgre bien avec Spring et permet de crĂ©er des vues dynamiques en utilisant des attributs spĂ©ciaux dans les balises HTML.

Pour utiliser Thymeleaf, ajoutez la dépendance spring-boot-starter-thymeleaf.

Exemple de template Thymeleaf (src/main/resources/templates/greeting.html):

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

Bootstrap est un framework CSS populaire pour dĂ©velopper des interfaces utilisateur responsives et modernes. Vous pouvez l’inclure dans vos templates Thymeleaf en ajoutant les liens vers les fichiers CSS et JS de Bootstrap dans la section <head> de votre HTML.

Validation des formulaires avec @Valid et BindingResult

Spring MVC intĂšgre la validation des donnĂ©es d’entrĂ©e Ă  l’aide de l’API Bean Validation (JSR 303/380). Vous pouvez utiliser des annotations comme @NotNull, @Size, @Email, etc., sur les champs de vos objets de formulaire (DTOs ou entitĂ©s).

Pour dĂ©clencher la validation, annotez l’argument de la mĂ©thode du contrĂŽleur avec @Valid ou @Validated. Utilisez l’objet BindingResult pour vĂ©rifier s’il y a des erreurs de validation.

Exemple :

import javax.validation.Valid;
import javax.validation.constraints.Size;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
 
public class MyForm {
    @Size(min = 1, max = 10)
    private String name;
 
    // Getters and setters
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
}
 
@Controller
public class FormController {
 
    @PostMapping("/submitForm")
    public String submitForm(@Valid @ModelAttribute("myForm") MyForm myForm, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            // Gérer les erreurs de validation
            return "form"; // Retourne la vue du formulaire avec les erreurs
        }
        // Traiter les données du formulaire
        return "success"; // Redirige vers une page de succĂšs
    }
}

Gestion des erreurs et des exceptions

Spring MVC offre plusieurs mécanismes pour gérer les erreurs et les exceptions :

  • Gestion globale des exceptions : Vous pouvez utiliser l’annotation @ControllerAdvice avec @ExceptionHandler pour gĂ©rer les exceptions de maniĂšre centralisĂ©e dans votre application.

    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.servlet.ModelAndView;
     
    @ControllerAdvice
    public class GlobalExceptionHandler {
     
        @ExceptionHandler(Exception.class)
        public ModelAndView handleException(Exception ex) {
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("errorMessage", ex.getMessage());
            return modelAndView;
        }
    }
  • Gestion des erreurs spĂ©cifiques au contrĂŽleur : Vous pouvez dĂ©finir des mĂ©thodes annotĂ©es avec @ExceptionHandler directement dans un contrĂŽleur pour gĂ©rer les exceptions spĂ©cifiques Ă  ce contrĂŽleur.

  • Pages d’erreur personnalisĂ©es : Vous pouvez configurer des pages d’erreur personnalisĂ©es pour diffĂ©rents codes de statut HTTP (par exemple, 404, 500) en crĂ©ant des fichiers de template nommĂ©s d’aprĂšs le code de statut dans le rĂ©pertoire src/main/resources/templates/error/ (par exemple, 404.html, 500.html).


5. AccÚs aux données avec Spring Data JPA

Introduction Ă  JPA et Hibernate

JPA (Java Persistence API) est une spécification Java qui définit une API standard pour la gestion de la persistance des données relationnelles dans les applications Java. Elle permet aux développeurs de mapper des objets Java à des tables de base de données (Object-Relational Mapping - ORM).

Hibernate est une implĂ©mentation populaire de la spĂ©cification JPA. C’est un framework ORM puissant qui fournit des fonctionnalitĂ©s supplĂ©mentaires au-delĂ  de la spĂ©cification JPA. Spring Data JPA utilise JPA et peut ĂȘtre configurĂ© pour utiliser Hibernate comme fournisseur de persistance par dĂ©faut.

Spring Data JPA simplifie considĂ©rablement l’implĂ©mentation des couches d’accĂšs aux donnĂ©es en fournissant des abstractions de haut niveau et en rĂ©duisant la quantitĂ© de code boilerplate nĂ©cessaire pour les opĂ©rations CRUD (Create, Read, Update, Delete).

CrĂ©ation d’entitĂ©s avec @Entity, @Id, @GeneratedValue

Dans JPA, les entitĂ©s sont des classes Java qui reprĂ©sentent des tables dans la base de donnĂ©es. Elles sont annotĂ©es avec @Entity. Chaque entitĂ© doit avoir une clĂ© primaire, gĂ©nĂ©ralement annotĂ©e avec @Id. L’annotation @GeneratedValue est utilisĂ©e pour spĂ©cifier la stratĂ©gie de gĂ©nĂ©ration de la clĂ© primaire (par exemple, auto-incrĂ©mentĂ©e).

Exemple d’entitĂ© simple :

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
 
@Entity
public class Product {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
    private double price;
 
    // Getters and setters
    public Long getId() {
        return id;
    }
 
    public void setId(Long id) {
        this.id = id;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public double getPrice() {
        return price;
    }
 
    public void setPrice(double price) {
        this.price = price;
    }
}

D’autres annotations JPA courantes incluent @Table, @Column, @Transient, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany pour dĂ©finir le mapping entre les objets et la base de donnĂ©es.

Définition de repositories avec JpaRepository

Spring Data JPA fournit une abstraction appelĂ©e “Repository” qui simplifie l’accĂšs aux donnĂ©es. En Ă©tendant l’interface JpaRepository, vous obtenez automatiquement des mĂ©thodes CRUD de base (comme save, findById, findAll, deleteById) sans avoir Ă  Ă©crire d’implĂ©mentation.

L’interface JpaRepository est gĂ©nĂ©rique et prend deux paramĂštres : le type de l’entitĂ© et le type de la clĂ© primaire.

Exemple de repository :

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
 
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    // Spring Data JPA générera automatiquement les méthodes CRUD
}

Spring Data JPA crĂ©e automatiquement une implĂ©mentation de cette interface au moment de l’exĂ©cution. Vous pouvez ensuite injecter ce repository dans vos services pour interagir avec la base de donnĂ©es.

RequĂȘtes personnalisĂ©es avec @Query et Query Methods

En plus des mĂ©thodes CRUD automatiques, Spring Data JPA permet de dĂ©finir des requĂȘtes personnalisĂ©es de deux maniĂšres principales :

  • Query Methods : Spring Data JPA peut gĂ©nĂ©rer automatiquement des requĂȘtes basĂ©es sur le nom des mĂ©thodes de l’interface du repository. En suivant une convention de nommage spĂ©cifique, vous pouvez crĂ©er des requĂȘtes complexes sans Ă©crire de code SQL ou JPQL.

    Exemples de Query Methods :

    • findByName(String name) : Trouve un produit par son nom.
    • findByPriceGreaterThan(double price) : Trouve les produits dont le prix est supĂ©rieur Ă  une valeur donnĂ©e.
    • findByNameContainingIgnoreCase(String name) : Trouve les produits dont le nom contient une chaĂźne de caractĂšres (insensible Ă  la casse).
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    import java.util.List;
     
    @Repository
    public interface ProductRepository extends JpaRepository<Product, Long> {
        List<Product> findByName(String name);
        List<Product> findByPriceGreaterThan(double price);
    }
  • @Query: Pour des requĂȘtes plus complexes qui ne peuvent pas ĂȘtre exprimĂ©es avec les Query Methods, vous pouvez utiliser l’annotation @Query pour dĂ©finir des requĂȘtes JPQL (Java Persistence Query Language) ou SQL natives.

    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.data.jpa.repository.Query;
    import org.springframework.stereotype.Repository;
    import java.util.List;
     
    @Repository
    public interface ProductRepository extends JpaRepository<Product, Long> {
     
        @Query("SELECT p FROM Product p WHERE p.name = ?1")
        List<Product> findProductByNameJPQL(String name);
     
        @Query(value = "SELECT * FROM product WHERE price > ?1", nativeQuery = true)
        List<Product> findProductsExpensiveSQL(double price);
    }

Gestion des transactions avec @Transactional

La gestion des transactions est essentielle pour garantir l’intĂ©gritĂ© des donnĂ©es lors des opĂ©rations sur la base de donnĂ©es. Spring offre une gestion dĂ©clarative des transactions Ă  l’aide de l’annotation @Transactional.

En annotant une méthode ou une classe avec @Transactional, vous indiquez à Spring de gérer la transaction pour cette méthode ou toutes les méthodes de la classe. Spring ouvrira une transaction au début de la méthode, exécutera le code, et commitera ou rollbackera la transaction en fonction du résultat (succÚs ou exception).

Exemple de service avec gestion transactionnelle :

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
@Service
public class ProductService {
 
    private final ProductRepository productRepository;
 
    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
 
    @Transactional
    public Product createProduct(Product product) {
        // Logique métier
        return productRepository.save(product);
    }
 
    @Transactional(readOnly = true)
    public Product getProductById(Long id) {
        return productRepository.findById(id).orElse(null);
    }
 
    // ... autres méthodes
}

L’attribut readOnly = true est utilisĂ© pour les opĂ©rations de lecture seule afin d’optimiser les performances.


6. DĂ©veloppement d’API RESTful

Principes REST : stateless, ressources, verbes HTTP

REST (Representational State Transfer) est un style d’architecture logicielle pour les systĂšmes hypermĂ©dia distribuĂ©s. Les principes clĂ©s de REST incluent :

  • Client-Server : SĂ©paration des prĂ©occupations entre l’interface utilisateur (client) et le stockage des donnĂ©es (serveur).
  • Stateless : Chaque requĂȘte du client au serveur doit contenir toutes les informations nĂ©cessaires pour comprendre et traiter la requĂȘte. Le serveur ne doit pas stocker d’informations sur l’état du client entre les requĂȘtes.
  • Cacheable : Les rĂ©ponses du serveur peuvent ĂȘtre mises en cache par le client pour amĂ©liorer les performances.
  • Layered System : L’architecture peut ĂȘtre composĂ©e de plusieurs couches, et chaque couche ne peut “voir” que la couche directement adjacente.
  • Code on Demand (Optional) : Le serveur peut Ă©tendre la fonctionnalitĂ© du client en tĂ©lĂ©chargeant et en exĂ©cutant du code (par exemple, JavaScript).
  • Uniform Interface : Un ensemble de contraintes architecturales qui simplifient et dĂ©couplent l’architecture, permettant Ă  chaque partie d’évoluer indĂ©pendamment. Les contraintes de l’interface uniforme incluent :
    • Identification des ressources : Les ressources sont identifiĂ©es par des URIs (Uniform Resource Identifiers).
    • Manipulation des ressources via des reprĂ©sentations : Les clients interagissent avec les ressources en utilisant des reprĂ©sentations de celles-ci (par exemple, JSON, XML).
    • Messages auto-descriptifs : Chaque message contient suffisamment d’informations pour dĂ©crire comment traiter le message.
    • Hypermedia as the Engine of Application State (HATEOAS) : Le client interagit avec l’application entiĂšrement via les hyperliens contenus dans les reprĂ©sentations des ressources.

Les API RESTful utilisent généralement les verbes HTTP standard pour effectuer des opérations sur les ressources :

  • GET : RĂ©cupĂ©rer une ressource ou une collection de ressources.
  • POST : CrĂ©er une nouvelle ressource.
  • PUT : Mettre Ă  jour une ressource existante.
  • DELETE : Supprimer une ressource.
  • PATCH : Appliquer des modifications partielles Ă  une ressource.

Création de services REST avec @RestController

Dans Spring Boot, la crĂ©ation d’API RESTful est simplifiĂ©e grĂące Ă  l’annotation @RestController. Comme mentionnĂ© prĂ©cĂ©demment, @RestController combine @Controller et @ResponseBody, ce qui signifie que les mĂ©thodes de cette classe retournent directement les donnĂ©es de rĂ©ponse (sĂ©rialisĂ©es en JSON ou XML par dĂ©faut) plutĂŽt que des noms de vues.

Exemple de contrĂŽleur REST simple :

import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
 
@RestController
@RequestMapping("/api/products")
public class ProductRestController {
 
    private List<Product> products = new ArrayList<>();
 
    // Initialisation (pour l'exemple)
    public ProductRestController() {
        products.add(new Product(1L, "Laptop", 1200.00));
        products.add(new Product(2L, "Mouse", 25.00));
    }
 
    @GetMapping
    public List<Product> getAllProducts() {
        return products;
    }
 
    @GetMapping("/{id}")
    public Product getProductById(@PathVariable Long id) {
        return products.stream()
                       .filter(p -> p.getId().equals(id))
                       .findFirst()
                       .orElse(null);
    }
 
    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        product.setId((long) (products.size() + 1));
        products.add(product);
        return product;
    }
 
    @PutMapping("/{id}")
    public Product updateProduct(@PathVariable Long id, @RequestBody Product updatedProduct) {
        for (int i = 0; i < products.size(); i++) {
            if (products.get(i).getId().equals(id)) {
                products.set(i, updatedProduct);
                return updatedProduct;
            }
        }
        return null; // Ou lancer une exception
    }
 
    @DeleteMapping("/{id}")
    public void deleteProduct(@PathVariable Long id) {
        products.removeIf(p -> p.getId().equals(id));
    }
}
 
// Classe Product (simplifiée pour l'exemple)
class Product {
    private Long id;
    private String name;
    private double price;
 
    public Product() {}
 
    public Product(Long id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
 
    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
}
  • @RequestMapping("/api/products"): DĂ©finit le chemin de base pour toutes les mĂ©thodes de ce contrĂŽleur.
  • @GetMapping, @PostMapping, etc. : Mappent les requĂȘtes HTTP spĂ©cifiques aux mĂ©thodes du contrĂŽleur.
  • @PathVariable: Extrait une partie de l’URI de la requĂȘte (par exemple, l’ID du produit).
  • @RequestBody: Indique qu’un paramĂštre de mĂ©thode doit ĂȘtre liĂ© au corps de la requĂȘte HTTP. Spring utilise un convertisseur de messages (comme Jackson) pour dĂ©sĂ©rialiser le corps de la requĂȘte en un objet Java.

Sérialisation et désérialisation JSON avec Jackson

Spring Boot utilise Jackson par dĂ©faut pour la sĂ©rialisation (conversion d’objets Java en JSON) et la dĂ©sĂ©rialisation (conversion de JSON en objets Java). Lorsque vous utilisez @RestController et retournez un objet Java, Spring Boot, avec l’aide de Jackson, convertit automatiquement cet objet en rĂ©ponse JSON. De mĂȘme, lorsque vous utilisez @RequestBody, Jackson dĂ©sĂ©rialise le corps de la requĂȘte JSON en l’objet Java spĂ©cifiĂ©.

Vous pouvez personnaliser le comportement de Jackson en ajoutant des annotations Jackson à vos classes de modùle (par exemple, @JsonIgnore, @JsonProperty) ou en configurant l’objet ObjectMapper de Jackson.

Gestion des codes de statut HTTP et des réponses personnalisées

Il est important de retourner les codes de statut HTTP appropriĂ©s dans vos API RESTful pour indiquer le rĂ©sultat de l’opĂ©ration. Spring MVC vous permet de contrĂŽler le code de statut HTTP de la rĂ©ponse.

  • Codes de statut par dĂ©faut : Par dĂ©faut, Spring retourne un code 200 OK pour les requĂȘtes rĂ©ussies. Pour les requĂȘtes POST, il retourne un code 200 OK ou 201 Created si une nouvelle ressource est créée.

  • @ResponseStatus: Vous pouvez utiliser cette annotation sur une mĂ©thode de contrĂŽleur ou une classe d’exception pour spĂ©cifier le code de statut HTTP de la rĂ©ponse.

    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ResponseStatus;
     
    @ResponseStatus(HttpStatus.CREATED)
    @PostMapping
    public Product createProduct(@RequestBody Product product) {
        // ...
        return product;
    }
     
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public class ProductNotFoundException extends RuntimeException {
        // ...
    }
  • ResponseEntity: Pour un contrĂŽle plus fin sur la rĂ©ponse (y compris les en-tĂȘtes HTTP et le corps de la rĂ©ponse), vous pouvez retourner un objet ResponseEntity depuis votre mĂ©thode de contrĂŽleur.

    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RestController;
     
    @RestController
    @RequestMapping("/api/products")
    public class ProductRestController {
     
        // ...
     
        @GetMapping("/{id}")
        public ResponseEntity<Product> getProductById(@PathVariable Long id) {
            Product product = products.stream()
                                   .filter(p -> p.getId().equals(id))
                                   .findFirst()
                                   .orElse(null);
            if (product != null) {
                return ResponseEntity.ok(product); // Code 200 OK avec le corps du produit
            } else {
                return ResponseEntity.notFound().build(); // Code 404 Not Found
            }
        }
    }

Sécurisation des API avec Spring Security et JWT

La sĂ©curisation des API RESTful est cruciale. Spring Security est un framework puissant et hautement configurable pour l’authentification et l’autorisation dans les applications Spring. Pour les API RESTful, une approche courante consiste Ă  utiliser l’authentification basĂ©e sur les tokens, comme JWT (JSON Web Tokens).

Les étapes générales pour sécuriser une API REST avec Spring Security et JWT incluent :

  1. Ajouter les dépendances Spring Security et JWT.
  2. Configurer Spring Security pour dĂ©sactiver la protection CSRF (Cross-Site Request Forgery) car elle n’est gĂ©nĂ©ralement pas nĂ©cessaire pour les API stateless.
  3. Configurer un filtre pour intercepter les requĂȘtes et valider les tokens JWT.
  4. ImplĂ©menter un mĂ©canisme d’authentification (par exemple, nom d’utilisateur/mot de passe) pour gĂ©nĂ©rer des tokens JWT lors de la connexion rĂ©ussie.
  5. ProtĂ©ger les endpoints de l’API en utilisant des annotations de sĂ©curitĂ© (par exemple, @PreAuthorize) ou une configuration de sĂ©curitĂ©.

C’est un sujet complexe qui nĂ©cessite une configuration dĂ©taillĂ©e, mais Spring Security fournit les blocs de construction nĂ©cessaires pour implĂ©menter une sĂ©curitĂ© robuste pour vos API REST.


7. Tests avec Spring Boot

Spring Boot facilite l’écriture de tests pour votre application en fournissant des annotations et des utilitaires de test pratiques. Une bonne stratĂ©gie de test inclut gĂ©nĂ©ralement des tests unitaires, des tests d’intĂ©gration et des tests de tranche (slice tests).

Tests unitaires avec JUnit 5 et Mockito

Les tests unitaires visent à tester de petites unités de code isolément, généralement des méthodes ou des classes individuelles, sans dépendances externes (comme une base de données ou un serveur web). JUnit 5 est le framework de test le plus couramment utilisé en Java, et Mockito est une bibliothÚque de mocking populaire pour créer des objets simulés (mocks) et vérifier les interactions.

Pour les tests unitaires, vous n’avez gĂ©nĂ©ralement pas besoin de dĂ©marrer le contexte Spring complet.

Exemple de test unitaire avec JUnit 5 et Mockito :

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
 
@ExtendWith(MockitoExtension.class)
public class MyServiceTest {
 
    @Mock
    private MyRepository myRepository;
 
    @InjectMocks
    private MyService myService;
 
    @Test
    void testProcessData() {
        when(myRepository.getData()).thenReturn("Mocked Data");
 
        String result = myService.processData();
 
        assertEquals("Processed: Mocked Data", result);
    }
}
 
// Classes simulées pour l'exemple
class MyRepository {
    public String getData() {
        return "Real Data";
    }
}
 
class MyService {
    private final MyRepository myRepository;
 
    public MyService(MyRepository myRepository) {
        this.myRepository = myRepository;
    }
 
    public String processData() {
        return "Processed: " + myRepository.getData();
    }
}
  • @ExtendWith(MockitoExtension.class): IntĂšgre Mockito avec JUnit 5.
  • @Mock: CrĂ©e un mock de l’interface ou de la classe spĂ©cifiĂ©e.
  • @InjectMocks: Injecte les mocks créés avec @Mock dans l’instance de la classe annotĂ©e.
  • when(...).thenReturn(...) : DĂ©finit le comportement d’une mĂ©thode mockĂ©e.

Tests d’intĂ©gration avec @SpringBootTest

Les tests d’intĂ©gration testent l’interaction entre plusieurs composants de l’application, voire l’application complĂšte avec ses dĂ©pendances externes (base de donnĂ©es, serveurs externes, etc.). L’annotation @SpringBootTest est l’annotation principale pour les tests d’intĂ©gration Spring Boot. Elle dĂ©marre un contexte Spring complet pour votre application.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertNotNull;
 
@SpringBootTest
public class MyprojectApplicationTests {
 
    @Autowired
    private MyService myService;
 
    @Test
    void contextLoads() {
        assertNotNull(myService);
    }
}

@SpringBootTest peut ĂȘtre configurĂ© avec diffĂ©rents attributs (par exemple, webEnvironment) pour contrĂŽler la maniĂšre dont le contexte Spring est chargĂ©.

Tests de contrĂŽleurs avec @WebMvcTest

@WebMvcTest est une annotation de test de tranche (slice test) qui se concentre sur les composants de la couche web (contrĂŽleurs). Elle dĂ©marre un contexte Spring limitĂ© qui ne contient que les beans pertinents pour tester les contrĂŽleurs (contrĂŽleurs, filtres, etc.), sans dĂ©marrer le serveur web complet ni charger d’autres couches (services, repositories).

Elle est souvent utilisĂ©e en combinaison avec MockMvc pour envoyer des requĂȘtes HTTP simulĂ©es aux contrĂŽleurs et vĂ©rifier les rĂ©ponses.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
 
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
 
@WebMvcTest(WebController.class) // Spécifie le contrÎleur à tester
public class WebControllerTests {
 
    @Autowired
    private MockMvc mockMvc;
 
    @Test
    void testGreeting() throws Exception {
        mockMvc.perform(get("/greeting?name=Test"))
               .andExpect(status().isOk())
               .andExpect(content().string("Hello, Test!"));
    }
}
  • @WebMvcTest(WebController.class): Configure le test pour se concentrer sur WebController.
  • MockMvc: Permet d’envoyer des requĂȘtes simulĂ©es.
  • mockMvc.perform(get("/greeting?name=Test")) : ExĂ©cute une requĂȘte GET simulĂ©e.
  • .andExpect(status().isOk()) : VĂ©rifie que le code de statut HTTP est 200 OK.
  • .andExpect(content().string("...")) : VĂ©rifie le contenu de la rĂ©ponse.

Tests de services avec @MockBean

Lors des tests de tranche ou d’intĂ©gration, vous pourriez vouloir simuler (mock) certaines dĂ©pendances pour isoler la couche que vous testez. L’annotation @MockBean est utilisĂ©e dans les tests Spring Boot pour ajouter des mocks Mockito au contexte de l’application Spring.

Cela est particuliĂšrement utile dans les tests @WebMvcTest ou @SpringBootTest oĂč vous voulez simuler les services ou les repositories pour tester la logique du contrĂŽleur ou d’autres composants sans interagir avec les dĂ©pendances rĂ©elles.

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
 
@SpringBootTest
public class MyServiceIntegrationTest {
 
    @MockBean // Simule MyRepository dans le contexte Spring
    private MyRepository myRepository;
 
    @Autowired
    private MyService myService;
 
    @Test
    void testProcessDataWithMockRepository() {
        when(myRepository.getData()).thenReturn("Mocked Data in Integration Test");
 
        String result = myService.processData();
 
        assertEquals("Processed: Mocked Data in Integration Test", result);
    }
}

Dans cet exemple, @MockBean remplace l’implĂ©mentation rĂ©elle de MyRepository par un mock Mockito dans le contexte Spring dĂ©marrĂ© par @SpringBootTest.

Utilisation de Postman pour tester les API

En plus des tests automatisĂ©s, il est souvent utile de tester manuellement les API RESTful Ă  l’aide d’outils comme Postman. Postman est un outil populaire qui permet d’envoyer des requĂȘtes HTTP (GET, POST, PUT, DELETE, etc.) Ă  vos endpoints d’API, d’inspecter les rĂ©ponses, de gĂ©rer les environnements et de crĂ©er des collections de requĂȘtes.

Pour tester une API Spring Boot avec Postman :

  1. Démarrez votre application Spring Boot.
  2. Ouvrez Postman.
  3. CrĂ©ez une nouvelle requĂȘte.
  4. Sélectionnez la méthode HTTP appropriée (GET, POST, etc.).
  5. Entrez l’URL de votre endpoint d’API (par exemple, http://localhost:8080/api/products).
  6. Si nĂ©cessaire, ajoutez des paramĂštres de requĂȘte, des en-tĂȘtes (par exemple, Content-Type: application/json), ou un corps de requĂȘte (pour les requĂȘtes POST ou PUT).
  7. Cliquez sur “Send” pour envoyer la requĂȘte.
  8. Inspectez la rĂ©ponse (code de statut, corps de la rĂ©ponse, en-tĂȘtes).

Postman est un outil précieux pour le développement et le débogage des API RESTful.


8. SĂ©curisation d’une application Spring Boot

La sĂ©curisation des applications web est une prĂ©occupation majeure. Spring Security est un framework puissant et flexible qui fournit des solutions complĂštes pour l’authentification et l’autorisation dans les applications Spring.

Introduction Ă  Spring Security

Spring Security est un framework de sĂ©curitĂ© dĂ©claratif qui s’intĂšgre facilement aux applications Spring. Il offre une large gamme de fonctionnalitĂ©s de sĂ©curitĂ©, notamment :

  • Authentification : VĂ©rification de l’identitĂ© d’un utilisateur.
  • Autorisation : DĂ©termination si un utilisateur authentifiĂ© a la permission d’accĂ©der Ă  une ressource ou d’effectuer une action.
  • Protection contre les attaques courantes : CSRF, XSS, etc.
  • IntĂ©gration avec d’autres technologies de sĂ©curitĂ© : LDAP, OAuth2, JWT, etc.

Spring Security utilise une chaĂźne de filtres pour intercepter les requĂȘtes et appliquer les rĂšgles de sĂ©curitĂ©. Vous configurez Spring Security en crĂ©ant des classes de configuration qui Ă©tendent WebSecurityConfigurerAdapter (dans les versions plus anciennes) ou en utilisant des classes de configuration basĂ©es sur des composants (dans les versions plus rĂ©centes avec Spring Security 5.x et au-delĂ ).

Authentification : en mémoire, en base de données, via LDAP

Spring Security prend en charge diverses mĂ©thodes d’authentification :

  • Authentification en mĂ©moire : Utile pour les applications simples ou les tests. Les informations d’identification des utilisateurs sont stockĂ©es directement en mĂ©moire.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.provisioning.InMemoryUserDetailsManager;
    import org.springframework.security.web.SecurityFilterChain;
     
    @Configuration
    @EnableWebSecurity
    public class InMemorySecurityConfig {
     
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests((requests) -> requests
                    .requestMatchers("/", "/home").permitAll()
                    .anyRequest().authenticated()
                )
                .formLogin((form) -> form
                    .loginPage("/login")
                    .permitAll()
                )
                .logout((logout) -> logout.permitAll());
     
            return http.build();
        }
     
        @Bean
        public UserDetailsService userDetailsService() {
            UserDetails user = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
            return new InMemoryUserDetailsManager(user);
        }
    }
  • Authentification en base de donnĂ©es : Les informations d’identification des utilisateurs sont stockĂ©es dans une base de donnĂ©es. Vous implĂ©mentez une interface UserDetailsService pour charger les dĂ©tails de l’utilisateur depuis la base de donnĂ©es.

  • Authentification via LDAP : IntĂ©gration avec un serveur LDAP (Lightweight Directory Access Protocol) pour l’authentification.

Autorisation : rĂŽles et permissions

Une fois qu’un utilisateur est authentifiĂ©, Spring Security dĂ©termine s’il est autorisĂ© Ă  accĂ©der Ă  une ressource ou Ă  effectuer une action. L’autorisation est gĂ©nĂ©ralement basĂ©e sur les rĂŽles ou les permissions attribuĂ©s Ă  l’utilisateur.

  • RĂŽles : Vous pouvez dĂ©finir des rĂŽles (par exemple, ROLE_USER, ROLE_ADMIN) et les attribuer aux utilisateurs. Vous pouvez ensuite configurer Spring Security pour autoriser l’accĂšs Ă  certaines URLs ou mĂ©thodes uniquement aux utilisateurs ayant des rĂŽles spĂ©cifiques.

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.web.SecurityFilterChain;
     
    @Configuration
    @EnableWebSecurity
    public class MethodSecurityConfig {
     
        @Bean
        public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            http
                .authorizeHttpRequests((requests) -> requests
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN")
                    .anyRequest().permitAll()
                );
            // ... autres configurations (formLogin, logout, etc.)
            return http.build();
        }
    }
  • Permissions : Pour un contrĂŽle d’autorisation plus fin, vous pouvez utiliser des permissions (par exemple, READ_PRODUCT, WRITE_PRODUCT) et les vĂ©rifier au niveau de la mĂ©thode Ă  l’aide d’annotations comme @PreAuthorize.

    import org.springframework.security.access.prepost.PreAuthorize;
    import org.springframework.stereotype.Service;
     
    @Service
    public class ProductService {
     
        @PreAuthorize("hasRole('ADMIN') or hasPermission('product', 'read')")
        public Product getProductById(Long id) {
            // ...
            return null;
        }
     
        @PreAuthorize("hasRole('ADMIN') or hasPermission('product', 'write')")
        public Product createProduct(Product product) {
            // ...
            return null;
        }
    }

Sécurisation des API REST avec JWT

Comme mentionné dans la section précédente, JWT est une approche courante pour sécuriser les API RESTful stateless. Le flux typique est le suivant :

  1. L’utilisateur s’authentifie (par exemple, avec nom d’utilisateur/mot de passe) auprùs d’un endpoint de connexion.
  2. Si l’authentification rĂ©ussit, le serveur gĂ©nĂšre un token JWT contenant des informations sur l’utilisateur (claims) et le renvoie au client.
  3. Le client stocke le token (par exemple, dans le stockage local du navigateur).
  4. Pour les requĂȘtes ultĂ©rieures vers les endpoints sĂ©curisĂ©s, le client inclut le token JWT dans l’en-tĂȘte Authorization (gĂ©nĂ©ralement au format Bearer <token>).
  5. Le serveur, configurĂ© avec Spring Security, intercepte la requĂȘte, extrait le token JWT, le valide (signature, expiration, etc.), et authentifie l’utilisateur en fonction des informations contenues dans le token.
  6. Spring Security applique ensuite les rĂšgles d’autorisation basĂ©es sur les rĂŽles ou permissions extraits du token.

L’implĂ©mentation de l’authentification JWT avec Spring Security implique la configuration de filtres personnalisĂ©s et l’utilisation d’une bibliothĂšque JWT (comme JJWT).

Gestion des sessions et des cookies

Pour les applications web traditionnelles basĂ©es sur des sessions, Spring Security gĂšre automatiquement les sessions et les cookies pour maintenir l’état d’authentification de l’utilisateur entre les requĂȘtes.

Pour les API RESTful stateless, il est gĂ©nĂ©ralement recommandĂ© de dĂ©sactiver la gestion des sessions cĂŽtĂ© serveur pour garantir que l’API est vĂ©ritablement stateless. Cela peut ĂȘtre fait dans la configuration de Spring Security :

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
 
@Configuration
@EnableWebSecurity
public class StatelessSecurityConfig {
 
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable() // Désactiver la protection CSRF pour les API stateless
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // Configurer la gestion des sessions
            .and()
            .authorizeHttpRequests((requests) -> requests
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/private/**").authenticated()
                .anyRequest().denyAll()
            );
        // ... ajouter des filtres JWT, etc.
        return http.build();
    }
}

En dĂ©finissant sessionCreationPolicy(SessionCreationPolicy.STATELESS), vous indiquez Ă  Spring Security de ne pas crĂ©er ni utiliser de sessions HTTP pour gĂ©rer l’état de sĂ©curitĂ©.


9. DĂ©ploiement d’une application Spring Boot

Spring Boot facilite le déploiement de vos applications grùce à ses fonctionnalités intégrées et à la possibilité de créer des fichiers exécutables autonomes.

CrĂ©ation d’un fichier exĂ©cutable .jar ou .war

La maniĂšre la plus courante de dĂ©ployer une application Spring Boot est de crĂ©er un fichier JAR exĂ©cutable (“fat JAR” ou “uber JAR”) qui contient toutes les dĂ©pendances nĂ©cessaires, y compris le serveur web embarquĂ©. Cela permet d’exĂ©cuter l’application avec un simple commande java -jar.

Si vous avez choisi l’empaquetage WAR lors de la crĂ©ation du projet (par exemple, pour dĂ©ployer sur un serveur d’applications externe comme Tomcat), vous obtiendrez un fichier .war.

Pour créer le fichier exécutable, utilisez votre outil de build :

  • Maven : ExĂ©cutez la commande suivante dans le rĂ©pertoire racine de votre projet :

    ./mvnw clean package

    Cela créera un fichier JAR exécutable dans le répertoire target/.

  • Gradle : ExĂ©cutez la commande suivante dans le rĂ©pertoire racine de votre projet :

    ./gradlew clean build

    Cela créera un fichier JAR exécutable dans le répertoire build/libs/.

Le fichier JAR exĂ©cutable peut ensuite ĂȘtre exĂ©cutĂ© Ă  partir de la ligne de commande :

java -jar your-application.jar

Déploiement sur un serveur Tomcat ou Jetty

Bien que Spring Boot inclue des serveurs embarquĂ©s, vous pouvez Ă©galement dĂ©ployer votre application sur un serveur d’applications externe comme Tomcat ou Jetty en empaquetant votre application sous forme de fichier WAR.

  1. Assurez-vous que votre projet est configuré pour produire un fichier WAR (dans pom.xml ou build.gradle).
  2. Construisez le projet pour générer le fichier WAR.
  3. DĂ©ployez le fichier WAR sur votre serveur d’applications (les Ă©tapes spĂ©cifiques dĂ©pendent du serveur d’applications).

Déploiement sur des plateformes cloud : Heroku, AWS, Azure

Spring Boot est bien adapté au déploiement sur des plateformes cloud. La nature autonome des fichiers JAR exécutables simplifie le processus.

  • Heroku : Heroku prend en charge nativement les applications Spring Boot. Vous pouvez dĂ©ployer votre application en poussant votre code vers un dĂ©pĂŽt Git Heroku. Heroku dĂ©tectera qu’il s’agit d’une application Spring Boot et la construira et la dĂ©ploiera automatiquement.
  • AWS (Amazon Web Services) : Vous pouvez dĂ©ployer des applications Spring Boot sur diverses services AWS, notamment :
    • Elastic Beanstalk : Un service facile Ă  utiliser pour dĂ©ployer et gĂ©rer des applications web.
    • EC2 (Elastic Compute Cloud) : Vous pouvez dĂ©ployer votre JAR exĂ©cutable sur une instance EC2.
    • ECS (Elastic Container Service) ou EKS (Elastic Kubernetes Service) : En utilisant Docker (voir ci-dessous).
  • Azure (Microsoft Azure) : Azure offre Ă©galement plusieurs options de dĂ©ploiement pour les applications Spring Boot, comme Azure App Service ou Azure Kubernetes Service (AKS).

Les Ă©tapes spĂ©cifiques varient en fonction de la plateforme cloud et du service choisi, mais l’idĂ©e gĂ©nĂ©rale est de fournir votre fichier JAR exĂ©cutable ou une image Docker de votre application Ă  la plateforme.

CrĂ©ation d’une image Docker pour l’application

Docker est une plateforme populaire pour le dĂ©veloppement, l’expĂ©dition et l’exĂ©cution d’applications dans des conteneurs. La crĂ©ation d’une image Docker pour votre application Spring Boot permet de la dĂ©ployer de maniĂšre cohĂ©rente dans diffĂ©rents environnements.

Vous aurez besoin d’un fichier Dockerfile dans le rĂ©pertoire racine de votre projet. Voici un exemple simple :

FROM openjdk:17-jdk-slim
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
  • FROM openjdk:17-jdk-slim : Utilise une image de base OpenJDK lĂ©gĂšre.
  • ARG JAR_FILE=target/*.jar : DĂ©finit un argument de build pour le chemin du fichier JAR.
  • COPY ${JAR_FILE} app.jar : Copie le fichier JAR construit dans le conteneur.
  • ENTRYPOINT ["java","-jar","/app.jar"] : DĂ©finit la commande Ă  exĂ©cuter lorsque le conteneur dĂ©marre.

Pour construire l’image Docker :

docker build -t my-spring-boot-app .

Pour exécuter le conteneur Docker :

docker run -p 8080:8080 my-spring-boot-app

Cela démarrera votre application Spring Boot dans un conteneur Docker, mappant le port 8080 du conteneur au port 8080 de votre machine locale.

Déploiement avec Kubernetes

Kubernetes est un systĂšme open source pour l’automatisation du dĂ©ploiement, de la mise Ă  l’échelle et de la gestion des applications conteneurisĂ©es. Si vous utilisez Docker, vous pouvez dĂ©ployer vos images Docker Spring Boot sur un cluster Kubernetes.

Cela implique généralement la création de fichiers de configuration YAML qui décrivent vos déploiements, services et autres ressources Kubernetes.

Exemple simplifiĂ© d’un fichier de dĂ©ploiement Kubernetes (deployment.yaml):

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-spring-boot-app
spec:
  replicas: 3 # Nombre d'instances de l'application
  selector:
    matchLabels:
      app: my-spring-boot-app
  template:
    metadata:
      labels:
        app: my-spring-boot-app
    spec:
      containers:
      - name: my-spring-boot-app
        image: my-spring-boot-app:latest # Nom de l'image Docker
        ports:
        - containerPort: 8080

Pour appliquer ce déploiement à votre cluster Kubernetes :

kubectl apply -f deployment.yaml

Le dĂ©ploiement avec Kubernetes est un sujet vaste, mais Spring Boot s’intĂšgre bien dans un pipeline de dĂ©ploiement conteneurisĂ© gĂ©rĂ© par Kubernetes.


10. Bonnes pratiques et outils complémentaires

Cette section couvre quelques bonnes pratiques et outils utiles pour le dĂ©veloppement d’applications Spring Boot.

Utilisation de Spring Boot DevTools pour le redémarrage automatique

Spring Boot DevTools est un ensemble d’outils qui amĂ©liorent l’expĂ©rience de dĂ©veloppement. L’une de ses fonctionnalitĂ©s les plus utiles est le redĂ©marrage automatique de l’application lorsque des fichiers sont modifiĂ©s. Cela accĂ©lĂšre considĂ©rablement le cycle de dĂ©veloppement en Ă©vitant d’avoir Ă  redĂ©marrer manuellement le serveur aprĂšs chaque changement de code.

Pour utiliser DevTools, ajoutez la dépendance suivante à votre pom.xml (Maven) ou build.gradle (Gradle) :

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <scope>runtime</scope>
    <optional>true</optional>
</dependency>

Gradle:

dependencies {
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
}

Avec cette dépendance, votre application redémarrera automatiquement chaque fois que vous compilez des classes ou modifiez des fichiers dans le classpath.

Surveillance de l’application avec Spring Boot Actuator

Spring Boot Actuator fournit des endpoints prĂȘts pour la production qui vous permettent de surveiller et de gĂ©rer votre application lorsqu’elle est dĂ©ployĂ©e. Il offre des informations sur l’état de santĂ© de l’application, les mĂ©triques, les informations de configuration, etc.

Pour utiliser Actuator, ajoutez la dépendance suivante :

Maven:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

Gradle:

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-actuator'
}

Une fois la dépendance ajoutée, plusieurs endpoints seront disponibles par défaut (par exemple, /actuator/health, /actuator/info). Vous pouvez configurer les endpoints exposés et leur sécurité via les fichiers de configuration (application.properties ou application.yml).

Gestion des logs avec SLF4J et Logback

Une bonne gestion des logs est essentielle pour le débogage et la surveillance des applications. Spring Boot utilise SLF4J (Simple Logging Facade for Java) comme façade de logging par défaut, avec Logback comme implémentation par défaut.

Vous pouvez configurer le logging via le fichier application.properties ou application.yml, ou en fournissant un fichier de configuration Logback (logback-spring.xml).

Exemple de configuration de logging dans application.properties:

logging.level.root=INFO
logging.level.org.springframework=INFO
logging.level.com.example.myapp=DEBUG
logging.file.name=myapp.log

Cela configure le niveau de logging pour diffĂ©rentes parties de l’application et spĂ©cifie un fichier de sortie pour les logs.

Documentation de l’API avec Swagger/OpenAPI

Documenter vos API RESTful est crucial pour les dĂ©veloppeurs qui les consomment. Swagger (maintenant partie de l’initiative OpenAPI) est un framework populaire pour concevoir, construire, documenter et consommer des API RESTful.

Spring Boot s’intĂšgre bien avec les implĂ©mentations d’OpenAPI comme Springdoc OpenAPI. En ajoutant la dĂ©pendance appropriĂ©e, Springdoc gĂ©nĂ©rera automatiquement la documentation de votre API basĂ©e sur les annotations de vos contrĂŽleurs.

Pour utiliser Springdoc OpenAPI, ajoutez la dépendance suivante :

Maven:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.0.2</version> <!-- Utilisez la derniĂšre version -->
</dependency>

Gradle:

dependencies {
    implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.0.2' // Utilisez la derniĂšre version
}

Une fois la dĂ©pendance ajoutĂ©e et l’application dĂ©marrĂ©e, vous pourrez accĂ©der Ă  l’interface utilisateur Swagger UI Ă  l’adresse http://localhost:8080/swagger-ui.html (par dĂ©faut) pour visualiser et interagir avec la documentation de votre API.

Gestion des dépendances avec Maven ou Gradle

Spring Boot s’appuie sur Maven ou Gradle pour la gestion des dĂ©pendances. Les “Starters” de Spring Boot simplifient la dĂ©claration des dĂ©pendances en fournissant des ensembles de dĂ©pendances prĂ©configurĂ©es et compatibles.

Assurez-vous de bien comprendre comment gĂ©rer les dĂ©pendances avec l’outil de build de votre choix, y compris la gestion des versions, les exclusions de dĂ©pendances et la crĂ©ation de profils de build.

En suivant ces bonnes pratiques et en utilisant les outils complémentaires appropriés, vous pouvez développer, tester, sécuriser et déployer vos applications Spring Boot de maniÚre plus efficace.