Hallo an alle. Wir beeilen uns, den Studenten zu ihrem Berufsurlaub zu gratulieren und ihnen mitzuteilen, dass wir im Februar den Kurs
"Developer on the Spring Framework" starten werden! Dies wird Gegenstand der heutigen Veröffentlichung sein.
Die Justice League ist in Gefahr und nur Alfred kann alle retten - mit dem neuen Managementsystem mit Spring Boot, Spring Data und MongoDB.
Für die Justice League sind dunkle Zeiten gekommen, in denen die großartige Darkside beschlossen hat, die Menschheit zu versklaven. Batman und Wonder Woman begannen, nach Mitgliedern der Liga zu suchen, und es fehlt nur ein wichtiger Punkt - das richtige Managementsystem für Mitglieder der Justice League.

Es ist nicht genug Zeit, um ein umfangreiches Projekt von Grund auf neu zu erstellen. Daher gibt Batman diese schwierige Aufgabe an seinen lieben Alfred weiter (Robin ist zu unberechenbar), der sich an etwas erinnert, das als Spring Boot bezeichnet wird. So können Sie keine Zeit damit verschwenden, kleine Projekt-Setup-Nuancen zu lösen und schnell zu wechseln zum Schreiben von Anwendungscode.
Also beginnt unser lieber Alfred mit Spring Boot, um schnell ein Mitgliederverwaltungssystem für die Justice League zu erstellen. Zumindest seine Back-End-Teile, da Batman direkt mit der REST-API arbeitet.
Es gibt viele bequeme Möglichkeiten, Spring Boot-Anwendungen zu konfigurieren. In diesem Artikel konzentrieren wir uns auf die traditionelle Methode, ein Paket (Spring CLI) herunterzuladen und unter Ubuntu von Grund auf neu einzurichten. Spring unterstützt auch das Online-Verpacken des Projekts mit seinem
Tool . Laden Sie hier die neueste stabile Version herunter. In diesem Artikel verwende ich Version 1.3.0.M1.
Konfigurieren Sie nach dem Entpacken des heruntergeladenen Archivs zunächst die folgenden Parameter im Profil:
SPRING_BOOT_HOME=<extracted path>/spring-1.3.0.M1 PATH=$SPRING_BOOT_HOME/bin:$PATH
Fügen Sie dann der bashrc-Datei Folgendes hinzu:
<extracted-path>/spring-1.3.0.M1/shell-completion/bash/spring
Dies fügt der Befehlszeile eine automatische Vervollständigung hinzu, wenn Sie mit spring-cli arbeiten, um Spring Boot-Anwendungen zu erstellen. Denken Sie daran, dass Sie zur Bestätigung der Änderungen die Profil- und bashrc-Dateien "quellen" müssen.
Dieser Artikel verwendet den folgenden Technologie-Stack:
- Frühlingsruhe
- Federdaten
- MongoDB.
Wir beginnen mit der Erstellung einer Anwendungsprojektvorlage, indem wir den folgenden Befehl ausführen. Bitte beachten Sie, dass ein Beispielprojekt hier von meinem GitHub-Repository heruntergeladen
werden kann .
spring init -dweb,data-mongodb,flapdoodle-mongo --groupId com.justiceleague --artifactId justiceleaguemodule --build maven justiceleaguesystem
Dadurch wird ein Maven-Projekt mit Spring MVC und Spring Data mit integrierter MongoDB generiert.
Standardmäßig erstellt spring-cli ein Projekt namens "Demo". Daher müssen wir die entsprechende generierte Anwendungsklasse umbenennen. Wenn Sie die oben genannten Quellen aus meinem GitHub-Repository verwendet haben, können Sie diesen Schritt überspringen.
Mit Spring Boot ist das Ausführen der Anwendung so einfach wie das Ausführen der vom Projekt erstellten JAR-Datei. Dies ruft nur die von @SpringBootApplication kommentierte Anwendungsklasse auf, um Spring zu laden. Mal sehen, wie es aussieht:
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); } }
Dann gehen wir zu den Domänenklassen über, in denen Spring Data zusammen mit MongoDB verwendet wird, um die Datenebene zu bestimmen. Die Domänenklasse lautet wie folgt:
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 ist intuitiv, insbesondere wenn Sie Erfahrung mit JPA / Hibernate haben. Anmerkungen sind sehr ähnlich. Das einzig Neue ist die Annotation.
@Document
Dies bezeichnet den Namen der Sammlung in der Mongo-Datenbank. Es gibt auch einen Index für Superheldennamen, da sich viele Abfragen auf die Suche nach Namen beziehen.
Spring Data führte die Funktionalität einer einfachen Repository-Definition ein, die sofort reguläre CRUD-Operationen und einige Leseoperationen unterstützt. Daher verwenden wir in unserer Anwendung die Funktionen der Spring Data-Repositorys sowie der Repository-Klasse wie folgt:
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); }
Standardspeichervorgänge werden über einen Proxy in die Spring-Laufzeit integriert, sodass Sie nur die Domänenklasse im Repository definieren müssen.
Wie Sie sehen, ist nur eine Methode definiert. Mit der Annotation
@Query
suchen wir einen Superhelden mit regulären Ausdrücken. Die Option "i" bedeutet, Groß- und Kleinschreibung zu ignorieren, wenn versucht wird, eine Übereinstimmung in MongoDB zu finden.
Anschließend fahren wir mit der Implementierung der Logik der Speicherung neuer Mitglieder der Justice League über das Service-Level fort.

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); } }
Auch hier ist es ganz einfach: Wenn der Teilnehmer bereits vorhanden ist, wird eine Fehlermeldung angezeigt. Andernfalls fügen Sie den Teilnehmer hinzu. Beachten Sie, dass hierfür die bereits implementierte Spring Data-Methode des Insert-Repositorys verwendet wird, die wir zuvor definiert haben.
Schließlich ist Alfred bereit, die neuen Funktionen zu zeigen, die er mithilfe von Spring REST über die REST-API entwickelt hat, sodass Batman Details über HTTP sendet - er ist ständig in Bewegung:
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 ist nicht genug, daher bieten wir Funktionen in Form einer JSON-Nutzlast an, obwohl Alfred altmodisch wäre und XML bevorzugen würde.
Unser alter Freund Alfred ist ein Handlanger von TDD (testgetriebene Entwicklung, übersetzt „Entwicklung durch Testen“), deshalb möchte er die Funktionalität testen. Wir sehen uns daher die Integrationstests von Alfred an, um sicherzustellen, dass die ursprüngliche Version des Managementsystems der Justice League korrekt und vorhersehbar ist. Bitte beachten Sie, dass hier nur REST-API-Tests angezeigt werden. Alfred hat ein größeres Volumen angenommen, das sich im
GitHub- Repository befindet.
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 {
Das ist wahrscheinlich alles. Mit Hilfe von Spring Boot konnte Alfred schnell ein minimal funktionierendes Justice League-Managementsystem mit einer REST-API erstellen. Im Laufe der Zeit werden wir die Funktionalität dieser Anwendung erweitern und sehen, wie Alfred einen Ansatz zur Bereitstellung der Anwendung über Docker auf einer von Kubernetes verwalteten Amazon AWS-Instanz findet. Spannende Zeiten erwarten uns, also verbinden Sie sich!
Traditionell warten wir auf Ihre Kommentare und laden Sie zu
einem offenen Webinar ein , das am 6. Februar vom Kandidaten der physikalischen und mathematischen Wissenschaften -
Yuri Dvorzhetsky -
abgehalten wird .