Introdução ao Spring Boot com o Spring Data Mongo

Olá pessoal. Nós nos apressamos em parabenizar os alunos por suas férias profissionais e em informar que em fevereiro iniciaremos o curso "Desenvolvedor no Spring Framework" ! Este será o assunto da publicação de hoje.

A Liga da Justiça está em perigo, e apenas Alfred pode salvar a todos - com o novo sistema de gerenciamento com Spring Boot, Spring Data e MongoDB.

Para a Liga da Justiça, os tempos sombrios chegaram quando o incrível Darkside decidiu escravizar a humanidade. Batman e Mulher Maravilha começaram a procurar por membros da Liga, e há apenas um ponto importante faltando - o sistema de gerenciamento adequado para os membros da Liga da Justiça.



Como não há tempo suficiente para criar um projeto volumoso do zero, o Batman passa essa difícil tarefa ao seu querido Alfred (Robin é imprevisível), que lembra algo chamado Spring Boot, que ajudará você a não perder tempo resolvendo pequenas nuances de configuração do projeto e alternando rapidamente para escrever o código do aplicativo.
Portanto, nosso querido Alfred começa a usar o Spring Boot para criar rapidamente um sistema de gerenciamento de membros da Liga da Justiça. No mínimo, suas partes de back-end, já que o Batman trabalha diretamente com a API REST.

Existem várias maneiras convenientes de configurar aplicativos Spring Boot. Neste artigo, focamos no método tradicional de baixar um pacote (Spring CLI) e configurá-lo do zero no Ubuntu. O Spring também suporta empacotar o projeto on-line com sua ferramenta . Baixe a última versão estável aqui . Neste artigo, estou usando a versão 1.3.0.M1.

Após descompactar o arquivo baixado, para iniciantes, configure os seguintes parâmetros no perfil:

SPRING_BOOT_HOME=<extracted path>/spring-1.3.0.M1 PATH=$SPRING_BOOT_HOME/bin:$PATH 

Em seguida, adicione o seguinte ao arquivo bashrc:

 <extracted-path>/spring-1.3.0.M1/shell-completion/bash/spring 

Isso adicionará o preenchimento automático à linha de comando ao trabalhar com o spring-cli para criar aplicativos Spring Boot. Lembre-se de que para confirmar as alterações, você precisa “originar” o perfil e os arquivos “bashrc”.

Este artigo usa a seguinte pilha de tecnologia:

  • REST de mola
  • Dados da Primavera
  • MongoDB.

Começamos criando um modelo de projeto de aplicativo executando o seguinte comando. Observe que um exemplo de projeto pode ser baixado do meu repositório GitHub aqui .

 spring init -dweb,data-mongodb,flapdoodle-mongo --groupId com.justiceleague --artifactId justiceleaguemodule --build maven justiceleaguesystem 

Isso irá gerar um projeto maven com o Spring MVC e o Spring Data com o MongoDB integrado.
Por padrão, o spring-cli cria um projeto chamado "Demo". Portanto, precisamos renomear a classe de aplicativo gerada correspondente. Se você usou as fontes do meu repositório GitHub mencionadas acima, pode pular esta etapa.
Usando o Spring Boot, a execução do aplicativo é tão simples quanto a execução do arquivo JAR criado pelo projeto. Que apenas chama a classe de aplicativo anotada por @SpringBootApplication para carregar o Spring. Vamos ver como fica:

 package com.justiceleague.justiceleaguemodule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** *   spring boot,   -    *   * * @author dinuka * */ @SpringBootApplication public class JusticeLeagueManagementApplication { public static void main(String[] args) { SpringApplication.run(JusticeLeagueManagementApplication.class, args); } } 

Em seguida, passamos às classes de domínio, nas quais o Spring Data, juntamente com o MongoDB, é usado para determinar o nível dos dados. A classe de domínio é a seguinte:

 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; /** *         ,  *    MongoDB * * @author dinuka * */ @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; } } 

O Spring Data é intuitivo, especialmente se você tiver experiência com o JPA / Hibernate. As anotações são muito semelhantes. A única coisa nova é a anotação.

 @Document 

que denota o nome da coleção no banco de dados Mongo. Há também um índice definido para nomes de super-heróis, pois muitas consultas estão relacionadas a pesquisas por nome.

A Spring Data introduziu a funcionalidade de uma definição simples de repositório, pronta para uso, que suporta operações CRUD regulares e algumas operações de leitura. Portanto, em nosso aplicativo, usamos os recursos dos repositórios Spring Data, bem como a classe de repositório da seguinte maneira:

 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 > { /** *        ,   . *  . * * @param superHeroName *        . * @return   {@link JusticeLeagueMemberDetail}   *  . */ @Query("{ 'name' : {$regex: ?0, $options: 'i' }}") JusticeLeagueMemberDetail findBySuperHeroName(final String superHeroName); } 

As operações de salvamento padrão são incorporadas ao tempo de execução do Spring por meio de um proxy, portanto, você só precisa definir a classe de domínio no repositório.

Como você pode ver, apenas um método está definido. Com a anotação @Query estamos procurando um super-herói usando expressões regulares. A opção "i" significa ignorar maiúsculas e minúsculas ao tentar encontrar uma correspondência no MongoDB.

Em seguida, prosseguimos com a implementação da lógica de armazenamento de novos membros da Liga da Justiça por meio do nível de serviço.



 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; /** *     {@link JusticeLeagueMemberService}   * ,     * * @author dinuka * */ @Service public class JusticeLeagueMemberServiceImpl implements JusticeLeagueMemberService { @Autowired private JusticeLeagueRepository justiceLeagueRepo; /** * {@inheritDoc} */ 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); } } 

Novamente, é bastante simples - se o participante já existe, lançamos um erro. Caso contrário, adicione o participante. Observe que isso usa o método Spring Data já implementado do repositório de inserção, que definimos anteriormente.

Por fim, Alfred está pronto para mostrar a nova funcionalidade que ele desenvolveu por meio da API REST usando o Spring REST, para que Batman comece a enviar detalhes via HTTP - ele está constantemente em movimento:

 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; /** *    REST API . * * @author dinuka * */ @RestController @RequestMapping("/justiceleague") public class JusticeLeagueManagementController { @Autowired private JusticeLeagueMemberService memberService; /** *             * * @param justiceLeagueMember *    . * @return an instance of {@link ResponseDTO}     *   . */ @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; } } 

Como o Batman não é suficiente, fornecemos funcionalidade na forma de uma carga JSON, embora Alfred seja antiquado e prefira XML.

Nosso velho amigo Alfred é um especialista em TDD (desenvolvimento orientado a testes, traduzido como "desenvolvimento através de testes"), então ele quer testar a funcionalidade. Por isso, examinamos os testes de integração escritos por Alfred para garantir que a versão inicial do sistema de gerenciamento da Liga da Justiça seja correta e previsível. Observe que aqui mostramos apenas testes da API REST. Alfred adotou um volume maior, que pode ser encontrado no repositório 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; /** *     ,     , *            . * * @author dinuka * */ @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); } /** *      mongodb     *  . * * @throws UnknownHostException * @throws IOException */ @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; /** *      REST-,  * {@link JusticeLeagueManagementController} * * @author dinuka * */ public class JusticeLeagueManagementControllerTest extends BaseIntegrationTest { /** *          *   . * * @throws Exception */ @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)); } /** *   ,       *    ,     . * * @throws Exception */ @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)); } /** *   ,      , *          JSON . *    -  . * * @throws Exception */ @Test public void testAddJusticeLeagueMemberWhenNameNotPassedIn() throws Exception { //     ,   //     . JusticeLeagueMemberDTO flash = new JusticeLeagueMemberDTO (null, "super speed", "Central City"); String jsonContent = mapper.writeValueAsString(flash); mockMvc.perform(MockMvcRequestBuilders.post("/justiceleague/addMember") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON).content(jsonContent)) .andExpect(MockMvcResultMatchers.status().is4xxClientError()); } } 

Provavelmente é tudo. Com a ajuda do Spring Boot, Alfred conseguiu criar rapidamente um sistema de gerenciamento da Liga da Justiça com funcionamento mínimo com uma API REST. Com o tempo, expandiremos a funcionalidade desse aplicativo e veremos como Alfred encontra uma abordagem para implantar o aplicativo através do Docker em uma instância do Amazon AWS gerenciada pelo Kubernetes. Tempos emocionantes nos aguardam, então conecte-se!

Tradicionalmente, aguardamos seus comentários e convidamos você para um webinar aberto , que será realizado em 6 de fevereiro pelo candidato de ciências físicas e matemáticas - Yuri Dvorzhetsky .

Source: https://habr.com/ru/post/pt437410/


All Articles