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; @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; @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 > { @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; @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); } }
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; @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; } }
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; @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 {
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 .