Comment et pourquoi construire un backend REST avec Spring Boot ?

Suite à une conférence à Mix-IT 2014, j’hésite sur la manière de construire mon backend REST. Il m’arrive régulièrement d’avoir à écrire un backend REST pour une simple page web-app. En règle générale, je génère dans un premier temps mon application avec Express installé sur ma machine.

Cependant, ce n’est peut-être pas la méthode la plus efficace pour moi…  Mon cœur de développeur balance entre un backend en NodeJS ou en Java grâce à Spring Boot. Sans trop de suspens, je vais mettre à l’épreuve la méthode avec Spring Boot.

Spring Boot nous apporte toute la puissance du framework Spring bien connu des développeurs Java, ainsi qu’un Tomcat embarqué et une documentation de référence expliquant clairement comment déployer une telle application dans le nuage, sur une plateforme telle que Heroku.

 

Installer Spring Boot

Spring Boot est très simple à utiliser. Tout ce que vous devez avoir est un JDK (au moins 7) et Maven ou Gradle. Une base de données peut être également utile pour stocker des données. Dans la suite de cet article, nous utiliserons Maven.

Ensuite, il suffit de créer un projet Maven comme vous le faites habituellement et d’ajouter cette configuration dans le fichier pom.xml.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
      http://maven.apache.org/xsd/maven-4.0.0.xsd
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.sqli</groupId>
   <artifactId>boottutorial</artifactId>
   <version>1.0-SNAPSHOT</version>

   <parent> <groupId>org.springframework.boot</groupId>

      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.1.4.RELEASE</version>
   </parent>
   <dependencies>
      <dependency> <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin> <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
               <source>1.8</source>
               <target>1.8</target>
            </configuration>
         </plugin>
      </plugins>
   </build>

</project>

Une fois que cela est fait, vous pouvez créer une classe principale qui sera appelée au démarrage, comme celle ci :

package com.sqli;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableAutoConfiguration
@ComponentScan
public class MainLauncher {
   public static void main(String[] args) throws Exception {
      SpringApplication.run(MainLauncher, args);
   }
}

L’annotation @Configuration indique à Spring que cette classe peut contenir de la configuration, @EnableAutoConfiguration fait toute la magie de Spring Boot : vous n’avez pas à vous soucier de l’initialisation des beans, du déploiement sur un Tomcat, de la configuration de base etc. Et l’annotation @ComponentScan initialise le component scanning que nous avions l’habitude de mettre dans le fichier de configuration Spring.

Votre application est désormais prête à être développée !

 

Créer son propre controller

Les entités

Maintenant que la base de l’application est prête, vous pouvez développer votre application Java comme vous l’auriez fait avec un Spring classique.

Pour cet article, j’ai décidé de créer une API qui permet de stocker des emplacements sur une carte HTML avec un nom et un nom abrégé.

Pour commencer, nous construisons une classe entité comme suit :

package com.sqli.domain;

import javax.persistence.*;

@Entity
public class Place {

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO) private
   Long id;

   @Column(unique = true, nullable = false) private
   String name;

   @Column(unique = true, nullable = false) private
   String shortName;

   private String coordinates;

   //Constructors, getters and setters are not shown here

}

Afin de stocker les données, nous utilisons PostGres. Mais vous pouvez choisir n’importe quelle base de votre choix (MySQL, Oracle, SQLServer, MongoDB etc.) Afin de configurer la connexion à notre base de données, nous ajoutons les lignes suivantes dans le fichier pom.xml

<!-- For the database connection -->
<dependency> <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>

<dependency> <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>9.1-901.jdbc4</version>

</dependency>

Et dans le dossier de ressources, nous ajoutons un fichier nommé application.yml. Ce fichier est automatiquement scanné par Spring Boot grâce à l’annotation @EnableAutoConfiguration.

---

spring: profile: dev 
   jpa:

      hibernate:
         ddl-auto: create-drop 
      datasource:
         platform: postgresql
         url: jdbc:postgresql://localhost/springboot 
         username: postgres
         password: postgres
         driverClassName: org.postgresql.Driver

---

Comme vous pouvez le voir, vous pouvez déclarer plusieurs profils dans ce fichier. Vous pouvez regarder la documentation officielle pour plus de détails sur ce point.

La couche DAO

Nous avons décidé d’utiliser Spring Data pour récupérer des données depuis notre base. Une interface comme celle qui suit va nous permettre de récupérer les données voulues :

package com.sqli.repositories;

import com.sqli.domain.Place;
import org.springframework.data.repository.CrudRepository;

public interface PlaceRepository extends CrudRepository<Place, Long> {
   Place findByShortName(String shortName);

}

L’avantage de Spring Data est que vous n’avez pas à écrire une quantité de méthodes pour seulement récupérer une donnée via son identifiant, son nom, ou supprimer ou mettre à jour cette donnée. Vous n’avez plus qu’à vous concentrer sur les cas plus complexes (ce qui n’est pas le cas dans l’exemple).

La couche service

Si vous voulez créer une application Spring avec toutes ses couches, vous devez créer une interface de service avec son implémentation. L’interface pourrait être la suivante :

package com.sqli.services; import
com.sqli.domain.Place; import
java.util.Collection; public interface
PlaceService {
   Collection<Place> getAllPlaces();
   Place getPlaceById(Long id);
  Place createPlace(Place place);
  Place updatePlace(Place place); void deletePlace(Long id);</pre>
   Place getPlaceByShortName(String shortName);
}

et l’implémentation :

package com.sqli.services.impl;
import com.sqli.domain.Place;
 import com.sqli.repositories.PlaceRepository;
 import com.sqli.services.PlaceService;
 import org.apache.commons.collections4.IteratorUtils;
 import org.springframework.stereotype.Service;
 import javax.annotation.Resource; import java.util.Collection;</pre>
@Service(value = "placeService")
 public class PlaceServiceImpl implements PlaceService {</pre>
@Resource
 private PlaceRepository placeRepository;</pre>
@Override
 public Collection<Place> getAllPlaces() {
 return IteratorUtils.toList(this.placeRepository.findAll().iterator());
 }
@Override
 public Place getPlaceById(Long id) {
 return this.placeRepository.findOne(id);
 }
@Override
 public Place createPlace(Place place) {
 return this.placeRepository.save(place);
 }
@Override
 public Place updatePlace(Place place) {
 return this.placeRepository.save(place);
 }
@Override
 public void deletePlace(Long id) {
 this.placeRepository.delete(id);
 }
@Override
 public Place getPlaceByShortName(String shortName) {
 return this.placeRepository.findByShortName(shortName);
 }
public PlaceRepository getPlaceRepository() {
 return placeRepository;
 }
public void setPlaceRepository(PlaceRepository placeRepository) {
 this.placeRepository = placeRepository;
 }
 }

Vous pouvez remarquer que tout le binding se fait via annotations.

Le contrôleur

Il ne reste plus qu’une seule chose à faire : le contrôleur.

Il s’agit d’un contrôleur classique pour ceux qui connaissent Spring MVC. Et grâce à Spring 4, il n’y a plus besoin d’effectuer la conversion Objet vers JSON et inversement. Le marshaling et le unmarshaling sont effectués par Spring et grâce à l’annotation @RestController.

package com.sqli.controllers;

import com.sqli.domain.Event;
import com.sqli.domain.Place;
import com.sqli.services.EventService;
import com.sqli.services.PlaceService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.Collection;

@RestController @RequestMapping(value =
"/places") public class PlaceController {

@Resource
private PlaceService placeService;
 
@RequestMapping(method = RequestMethod.POST)
public Place createPlace(@RequestBody Place place) {

   return this.placeService.createPlace(place);
}

@RequestMapping(method = RequestMethod.GET)
public Collection<Place> getAllPlaces() {

   return this.placeService.getAllPlaces();
}

@RequestMapping(value = "/{shortName}", method = RequestMethod.GET)
public Place getPlaceForShortName(@PathVariable(value = "shortName")
String shortName) {

   //find place by shortname
   return this.placeService.getPlaceByShortName(shortName);

}

@RequestMapping(value = "/{id}", method = RequestMethod.DELETE) public void
deletePlace(@PathVariable(value = "id") Long id) {
this.placeService.deletePlace(id);

}

@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public Place updatePlace(@PathVariable(value = "id") Long id, @RequestBody
Place place) {

   place.setId(id);

   return this.placeService.updatePlace(place);

}

public PlaceService getPlaceService() { return
   placeService;
}

public void setPlaceService(PlaceService placeService) {
   this.placeService = placeService;
   }

}

Filtre CORS

Si vous devez écrire une API publique, vous devez autoriser les requêtes Cross Domain. Pour cela, nous avons écrit une classe très simple qui autorise toutes les requêtes quelle que soit la source :

package com.sqli.filters;

import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class CorsFilter extends OncePerRequestFilter {

   @Override
   protected void doFilterInternal(HttpServletRequest httpServletRequest,
   HttpServletResponse httpServlet

   httpServletResponse.addHeader("Access-Control-Allow-Origin", "*");
   httpServletResponse.addHeader("Access-Control-Allow-Methods",
   "GET, POST, PUT, DELETE, OPTIONS");
   httpServletResponse.addHeader("Access-Control-Allow-Headers",
   "origin, content-type, accept,
   x-req filterChain.doFilter(httpServletRequest, httpServletResponse);

   }
}

Et parce que nous utilisons l’annotation @ComponentScan, notre filtre sera automatiquement chargé et utilisé à chaque requête.

 

Lancement de l’application

Maintenant que nous avons écrit tout le code, il ne nous reste plus qu’à lancer l’application. Pour cela, lancez la classe MainLauncher en tant qu’application Java.

Si vous vous rendez sur la page http://localhost:8080/places, vous aurez la liste de tous les lieux stockés dans votre base de données. Vous pouvez utiliser un client tel que Postman afin de requêter, ajouter, modifier ou supprimer des données en base.

 

Conclusion

En tant que développeur Java, je trouve que Spring Boot permet de créer rapidement et simplement une application sans avoir à écrire de nombreux fichiers XML complexes, sans avoir de server Java sur soi etc. Spring Boot peut donc être utilisé pour prototyper votre backend REST sans trop de difficultés mais peut également être utilisé afin de concevoir des applications Spring plus complexes.

 

8 commentaires
  1. Cédric Nicoloso dit :

    Bonjour Matthis !

    Ravi de lire un article de ta part sur ce blog 🙂 Et sur un sujet super intéressant en plus.

    Je l’ai lu et « appliqué » jusqu’au bout, j’en profite pour noter mes retours :

    – J’ai eu besoin de rajouter .class dans le run de Spring boot : SpringApplication.run(MainLauncher.class, args); C’est peut-être du à des versions différentes de Spring Boot, je n’ai pas fouillé…

    – Dans le fichier application.yml, je pense que la datasource doit être directement en-dessous de « spring » et non de « jpa » (Mais c’est peut-être du au copier/coller de ton article de base vers le blog !)

    – Il faut bien sur la dépendance Apache Commons Collections pour utiliser le IteratorUtils :

    org.apache.commons
    commons-collections4
    4.0

    – Typo sur l’URL à attaquer : http://localhost:8080/places avec un ‘s’ comme défini dans @RestController@RequestMapping(value = « /places ») Rien de grave…

    – Et encore rien de grave (typo) : « vous aurez la liste de toutes les lieux stockés dans votre base de données. »
    de TOUS les lieux…

    Et tout fonctionne super bien 🙂
    (Je me suis permis d’utiliser un MariaDB à la place de PostGres, j’espère que tu ne m’en veux pas !)

    Merci encore pour ce partage et vivement le prochain.

    Cédric N.

    Répondre
    • dinaa Ghandi dit :

      merci pour les retours, ils m’étaient d’une grande aide.
      je n’ai pas compris le but, nous n’avons utilisé aucune vue. et concernant les données?

      Répondre
  2. asic dit :

    Clairement, rien ne fonctionne dans le code et la configuration présentés.
    Il n’y a pas non plus de lien vers github (ou autre) permettant de récupérer le code en entier.
    Peut faire mieux !!!

    Répondre
    • tom dit :

      Contrairement à vous, j’ai beaucoup apprécié ce tutorial. Si vous preniez le temps de réfléchir plutôt que de copier/coller bêtement, vous auriez vue toute l’utilité de cet article. Bref, « peut mieux faire » s’applique surement mieux à vous qu’à Matthis.
      Merci Matthis.

      Répondre
  3. Pascal dit :

    C’est dommage,
    tant qu’à prendre la peine de faire un tutoriel, ou proposer un exemple, autant produire quelque chose qui fonctionne.
    Il y a tellement d’articles ‘incomplets’ sur le web.

    « Faire réfléchir à la place de faire des copier-coller » : si je voulais réfléchir je lirai la doc Spring.
    Là, je voudrais un truc qui fonctionne rapidement.
    Libre à moi après de creuser et réfléchir aussi longtemps que souhaité.

    Répondre
  4. sou dit :

    Bonjour, svp j’ai une question est ce que je dois créer la table Place manuellement dans la base de données ou c’est automatique car j’ai un erreur: La table « springboot.place » n’existe pas.
    Merci pour votre aide 🙂

    Répondre

votre commentaire

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Inscription newsletter

Ne manquez plus nos derniers articles !