Bonjour à tous. Nous nous empressons de féliciter les étudiants pour leurs vacances professionnelles et de vous informer qu'en février nous commencerons le cours
"Developer on the Spring Framework" ! Ce sera le sujet de la publication d'aujourd'hui.
La Justice League est en danger, et seul Alfred peut sauver tout le monde - avec le nouveau système de gestion avec Spring Boot, Spring Data et MongoDB.
Pour Justice League, les temps sombres sont venus où l'impressionnant Darkside a décidé d'asservir l'humanité. Batman et Wonder Woman ont commencé à rechercher des membres de la Ligue, et il ne manque qu'un point important: le système de gestion approprié pour les membres de la Ligue de justice.

Il n'y a pas assez de temps pour créer un projet volumineux à partir de zéro, alors Batman passe cette tâche difficile à son cher Alfred (Robin est trop imprévisible), qui se souvient de quelque chose appelé Spring Boot, qui vous aidera à ne pas perdre de temps à résoudre des nuances de configuration de petits projets et à basculer rapidement à l'écriture du code d'application.
Notre cher Alfred commence donc à utiliser Spring Boot pour créer rapidement un système de gestion des membres de la Justice League. Au minimum, ses parties principales, car Batman fonctionne directement avec l'API REST.
Il existe de nombreuses façons pratiques de configurer les applications Spring Boot. Dans cet article, nous nous concentrerons sur la méthode traditionnelle de téléchargement d'un package (Spring CLI) et de sa configuration à partir de zéro sur Ubuntu. Spring prend également en charge le packaging du projet en ligne avec leur
outil . Téléchargez la dernière version stable
ici . Dans cet article, j'utilise la version 1.3.0.M1.
Après avoir déballé l'archive téléchargée, pour commencer, configurez les paramètres suivants dans le profil:
SPRING_BOOT_HOME=<extracted path>/spring-1.3.0.M1 PATH=$SPRING_BOOT_HOME/bin:$PATH
Ajoutez ensuite ce qui suit au fichier bashrc:
<extracted-path>/spring-1.3.0.M1/shell-completion/bash/spring
Cela ajoutera l'auto-complétion à la ligne de commande lorsque vous travaillez avec spring-cli pour créer des applications Spring Boot. N'oubliez pas que pour confirmer les modifications dont vous avez besoin pour «source» le profil et les fichiers «bashrc».
Cet article utilise la pile technologique suivante:
- Spring REST
- Données de printemps
- MongoDB.
Nous commençons par créer un modèle de projet d'application en exécutant la commande suivante. Veuillez noter qu'un exemple de projet peut être téléchargé depuis mon référentiel GitHub
ici .
spring init -dweb,data-mongodb,flapdoodle-mongo --groupId com.justiceleague --artifactId justiceleaguemodule --build maven justiceleaguesystem
Cela va générer un projet maven avec Spring MVC et Spring Data avec MongoDB intégré.
Par défaut, spring-cli crée un projet appelé «Demo». Par conséquent, nous devons renommer la classe d'application générée correspondante. Si vous avez utilisé les sources de mon référentiel GitHub mentionnées ci-dessus, vous pouvez ignorer cette étape.
À l'aide de Spring Boot, l'exécution de l'application est aussi simple que l'exécution du fichier JAR créé par le projet. Ce qui appelle simplement la classe d'application annotée par @SpringBootApplication pour charger Spring. Voyons à quoi ça ressemble:
package com.justiceleague.justiceleaguemodule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class JusticeLeagueManagementApplication { public static void main(String[] args) { SpringApplication.run(JusticeLeagueManagementApplication.class, args); } }
Ensuite, nous passons aux classes de domaine, où Spring Data et MongoDB sont utilisés pour déterminer le niveau de données. La classe de domaine est la suivante:
package com.justiceleague.justiceleaguemodule.domain; import org.bson.types.ObjectId; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "justiceLeagueMembers") public class JusticeLeagueMemberDetail { @Id private ObjectId id; @Indexed private String name; private String superPower; private String location; public JusticeLeagueMemberDetail(String name, String superPower, String location) { this.name = name; this.superPower = superPower; this.location = location; } public String getId() { return id.toString(); } public void setId(String id) { this.id = new ObjectId(id); } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSuperPower() { return superPower; } public void setSuperPower(String superPower) { this.superPower = superPower; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } }
Spring Data est intuitif, surtout si vous avez de l'expérience avec JPA / Hibernate. Les annotations sont très similaires. La seule nouveauté est l'annotation.
@Document
qui indique le nom de la collection dans la base de données Mongo. Il existe également un index défini pour les noms de super-héros, car de nombreuses requêtes concernent des recherches par nom.
Spring Data a introduit la fonctionnalité d'une définition de référentiel simple, prête à l'emploi prenant en charge les opérations CRUD régulières et certaines opérations de lecture. Ainsi, dans notre application, nous utilisons les capacités des référentiels Spring Data, ainsi que la classe de référentiel comme suit:
package com.justiceleague.justiceleaguemodule.dao; import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.Query; import com.justiceleague.justiceleaguemodule.domain.JusticeLeagueMemberDetail; public interface JusticeLeagueRepository extends MongoRepository < JusticeLeagueMemberDetail, String > { @Query("{ 'name' : {$regex: ?0, $options: 'i' }}") JusticeLeagueMemberDetail findBySuperHeroName(final String superHeroName); }
Les opérations de sauvegarde standard sont intégrées dans le runtime de Spring via un proxy, il vous suffit donc de définir la classe de domaine dans le référentiel.
Comme vous pouvez le voir, une seule méthode est définie. Avec l'annotation
@Query
nous recherchons un super-héros utilisant des expressions régulières. L'option «i» signifie ignorer la casse lorsque vous essayez de trouver une correspondance dans MongoDB.
Ensuite, nous procédons à la mise en place de la logique de stockage des nouveaux membres de la Justice League à travers le niveau de service.

package com.justiceleague.justiceleaguemodule.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.justiceleague.justiceleaguemodule.constants.MessageConstants.ErrorMessages; import com.justiceleague.justiceleaguemodule.dao.JusticeLeagueRepository; import com.justiceleague.justiceleaguemodule.domain.JusticeLeagueMemberDetail; import com.justiceleague.justiceleaguemodule.exception.JusticeLeagueManagementException; import com.justiceleague.justiceleaguemodule.service.JusticeLeagueMemberService; import com.justiceleague.justiceleaguemodule.web.dto.JusticeLeagueMemberDTO; import com.justiceleague.justiceleaguemodule.web.transformer.DTOToDomainTransformer; @Service public class JusticeLeagueMemberServiceImpl implements JusticeLeagueMemberService { @Autowired private JusticeLeagueRepository justiceLeagueRepo; public void addMember(JusticeLeagueMemberDTO justiceLeagueMember) { JusticeLeagueMemberDetail dbMember = justiceLeagueRepo.findBySuperHeroName(justiceLeagueMember.getName()); if (dbMember != null) { throw new JusticeLeagueManagementException(ErrorMessages.MEMBER_ALREDY_EXISTS); } JusticeLeagueMemberDetail memberToPersist = DTOToDomainTransformer.transform(justiceLeagueMember); justiceLeagueRepo.insert(memberToPersist); } }
Encore une fois, c'est assez simple - si le participant existe déjà, alors nous lançons une erreur. Sinon, ajoutez le participant. Notez que cela utilise la méthode Spring Data déjà implémentée du référentiel d'insertion, que nous avons définie précédemment.
Enfin, Alfred est prêt à montrer les nouvelles fonctionnalités qu'il a développées via l'API REST à l'aide de Spring REST, afin que Batman commence à envoyer des détails via HTTP - il est constamment en mouvement:
package com.justiceleague.justiceleaguemodule.web.rest.controller; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.justiceleague.justiceleaguemodule.constants.MessageConstants; import com.justiceleague.justiceleaguemodule.service.JusticeLeagueMemberService; import com.justiceleague.justiceleaguemodule.web.dto.JusticeLeagueMemberDTO; import com.justiceleague.justiceleaguemodule.web.dto.ResponseDTO; @RestController @RequestMapping("/justiceleague") public class JusticeLeagueManagementController { @Autowired private JusticeLeagueMemberService memberService; @ResponseBody @ResponseStatus(value = HttpStatus.CREATED) @RequestMapping(method = RequestMethod.POST, path = "/addMember", produces = { MediaType.APPLICATION_JSON_VALUE }, consumes = { MediaType.APPLICATION_JSON_VALUE }) public ResponseDTO addJusticeLeagueMember(@Valid @RequestBody JusticeLeagueMemberDTO justiceLeagueMember) { ResponseDTO responseDTO = new ResponseDTO(ResponseDTO.Status.SUCCESS, MessageConstants.MEMBER_ADDED_SUCCESSFULLY); try { memberService.addMember(justiceLeagueMember); } catch (Exception e) { responseDTO.setStatus(ResponseDTO.Status.FAIL); responseDTO.setMessage(e.getMessage()); } return responseDTO; } }
Batman ne suffit pas, nous fournissons donc des fonctionnalités sous la forme d'une charge utile JSON, bien qu'Alfred soit à l'ancienne et préfère XML.
Notre vieil ami Alfred est un homme de main de TDD (développement piloté par les tests, traduit «développement par le biais de tests»), il veut donc tester la fonctionnalité. Et donc nous regardons les tests d'intégration écrits par Alfred pour nous assurer que la version initiale du système de gestion de Justice League est correcte et prévisible. Veuillez noter qu'ici, nous affichons uniquement les tests de l'API REST. Alfred a adopté un volume plus important, qui peut être trouvé dans le référentiel
GitHub .
package com.justiceleague.justiceleaguemodule.test.util; import java.io.IOException; import java.net.UnknownHostException; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import com.fasterxml.jackson.databind.ObjectMapper; import com.justiceleague.justiceleaguemodule.domain.JusticeLeagueMemberDetail; import de.flapdoodle.embed.mongo.MongodExecutable; import de.flapdoodle.embed.mongo.MongodStarter; import de.flapdoodle.embed.mongo.config.IMongodConfig; import de.flapdoodle.embed.mongo.config.MongodConfigBuilder; import de.flapdoodle.embed.mongo.config.Net; import de.flapdoodle.embed.mongo.distribution.Version; @RunWith(SpringRunner.class) @SpringBootTest @AutoConfigureMockMvc public abstract class BaseIntegrationTest { @Autowired protected MockMvc mockMvc; protected ObjectMapper mapper; private static MongodExecutable mongodExecutable; @Autowired protected MongoTemplate mongoTemplate; @Before public void setUp() { mapper = new ObjectMapper(); } @After public void after() { mongoTemplate.dropCollection(JusticeLeagueMemberDetail.class); } @BeforeClass public static void beforeClass() throws UnknownHostException, IOException { MongodStarter starter = MongodStarter.getDefaultInstance(); IMongodConfig mongoConfig = new MongodConfigBuilder().version(Version.Main.PRODUCTION) .net(new Net(27017, false)).build(); mongodExecutable = starter.prepare(mongoConfig); try { mongodExecutable.start(); } catch (Exception e) { closeMongoExecutable(); } } @AfterClass public static void afterClass() { closeMongoExecutable(); } private static void closeMongoExecutable() { if (mongodExecutable != null) { mongodExecutable.stop(); } } }
package com.justiceleague.justiceleaguemodule.web.rest.controller; import org.hamcrest.beans.SamePropertyValuesAs; import org.junit.Assert; import org.junit.Test; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import com.justiceleague.justiceleaguemodule.constants.MessageConstants; import com.justiceleague.justiceleaguemodule.constants.MessageConstants.ErrorMessages; import com.justiceleague.justiceleaguemodule.domain.JusticeLeagueMemberDetail; import com.justiceleague.justiceleaguemodule.test.util.BaseIntegrationTest; import com.justiceleague.justiceleaguemodule.web.dto.JusticeLeagueMemberDTO; import com.justiceleague.justiceleaguemodule.web.dto.ResponseDTO; import com.justiceleague.justiceleaguemodule.web.dto.ResponseDTO.Status; public class JusticeLeagueManagementControllerTest extends BaseIntegrationTest { @Test public void testAddJusticeLeagueMember() throws Exception { JusticeLeagueMemberDTO flash = new JusticeLeagueMemberDTO("Barry Allen", "super speed", "Central City"); String jsonContent = mapper.writeValueAsString(flash); String response = mockMvc .perform(MockMvcRequestBuilders.post("/justiceleague/addMember") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON).content(jsonContent)) .andExpect(MockMvcResultMatchers.status().isCreated()).andReturn().getResponse().getContentAsString(); ResponseDTO expected = new ResponseDTO(Status.SUCCESS, MessageConstants.MEMBER_ADDED_SUCCESSFULLY); ResponseDTO receivedResponse = mapper.readValue(response, ResponseDTO.class); Assert.assertThat(receivedResponse, SamePropertyValuesAs.samePropertyValuesAs(expected)); } @Test public void testAddJusticeLeagueMemberWhenMemberAlreadyExists() throws Exception { JusticeLeagueMemberDetail flashDetail = new JusticeLeagueMemberDetail("Barry Allen", "super speed","Central City"); mongoTemplate.save(flashDetail); JusticeLeagueMemberDTO flash = new JusticeLeagueMemberDTO("Barry Allen", "super speed", "Central City"); String jsonContent = mapper.writeValueAsString(flash); String response = mockMvc .perform(MockMvcRequestBuilders.post("/justiceleague/addMember"). accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON).content(jsonContent)) .andExpect(MockMvcResultMatchers.status().isCreated()).andReturn().getResponse() .getContentAsString(); ResponseDTO expected = new ResponseDTO(Status.FAIL, ErrorMessages.MEMBER_ALREDY_EXISTS); ResponseDTO receivedResponse = mapper.readValue(response, ResponseDTO.class); Assert.assertThat(receivedResponse, SamePropertyValuesAs.samePropertyValuesAs(expected)); } @Test public void testAddJusticeLeagueMemberWhenNameNotPassedIn() throws Exception {
C’est probablement tout. Avec l'aide de Spring Boot, Alfred a pu créer rapidement un système de gestion Justice League fonctionnant au minimum avec une API REST. Au fil du temps, nous élargirons les fonctionnalités de cette application et verrons comment Alfred trouve une approche pour déployer l'application via Docker sur une instance Amazon AWS gérée par Kubernetes. Des moments passionnants nous attendent, alors connectez-vous!
Traditionnellement, nous attendons vos commentaires et sommes invités à
un webinaire ouvert , qui se tiendra le 6 février par le candidat des sciences physiques et mathématiques -
Yuri Dvorzhetsky .