Gruß, Chabrowsk. Die Übersetzung des zweiten Teils eines Artikels, der speziell für Studenten des Kurses "Developer on the Spring Framework" erstellt wurde, kam also rechtzeitig an. Der erste Teil kann hier gelesen werden .
Spring ist vielleicht eine der beliebtesten Java-Entwicklungsplattformen. Dies ist ein mächtiges, aber ziemlich schwer zu erlernendes Werkzeug. Die grundlegenden Konzepte sind relativ einfach zu verstehen und zu erlernen, aber es erfordert Zeit und Mühe, ein erfahrener Spring-Entwickler zu werden.
In diesem Artikel werden einige der häufigsten Fehler bei der Arbeit in Spring behandelt, die sich insbesondere auf die Entwicklung von Webanwendungen und die Verwendung der Spring Boot-Plattform beziehen. Wie auf der Spring Boot-Website erwähnt , verfolgt Spring Boot einen standardisierten Ansatz zum Erstellen gebrauchsfertiger Anwendungen. Dieser Artikel folgt diesem Ansatz. Es enthält eine Reihe von Empfehlungen, die bei der Entwicklung von Standard-Webanwendungen auf Basis von Spring Boot effektiv verwendet werden können.
Falls Sie mit der Spring Boot-Plattform nicht sehr vertraut sind, aber mit den Beispielen in diesem Artikel experimentieren möchten, habe ich ein GitHub-Repository mit zusätzlichen Materialien für diesen Artikel erstellt . Wenn Sie beim Lesen dieses Artikels irgendwann etwas verwirrt sind, empfehle ich Ihnen, einen Klon dieses Repositorys zu erstellen und mit dem Code auf Ihrem Computer zu experimentieren.
Häufiger Fehler Nr. 6: Keine annotationsbasierte Datenvalidierung verwenden
Stellen wir uns vor, unser TopTalent-Service aus den vorherigen Beispielen benötigt einen Endpunkt, um neue TopTalent-Daten hinzuzufügen. Nehmen wir außerdem an, dass aus einem wirklich wichtigen Grund jeder Name, den Sie hinzufügen, genau 10 Zeichen lang sein muss. Dies kann beispielsweise wie folgt implementiert werden:
@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) {
Der obige Code ist jedoch nicht nur schlecht strukturiert, sondern auch keine „saubere“ Lösung. Wir führen verschiedene Arten der Datenüberprüfung durch (
TopTalentData
wir überprüfen, ob das
TopTalentData
Objekt nicht null ist, ob der Wert des Felds TopTalentData.name nicht null ist und ob die Länge des
TopTalentData.name
10 Zeichen beträgt) und
TopTalentData.name
auch eine Ausnahme aus, wenn die Daten falsch sind.
All dies kann mit dem
Hibernate-Validator im Frühjahr genauer durchgeführt werden.
addTopTalent
wir zunächst die
addTopTalent
Methode neu und unterstützen die Datenvalidierung:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {
Darüber hinaus müssen wir angeben, welche Eigenschaftsüberprüfung in der
TopTalentData
Klasse durchgeführt werden soll:
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
Spring fängt jetzt die Anforderung ab und überprüft sie, bevor die Methode aufgerufen wird, sodass keine zusätzlichen manuellen Überprüfungen erforderlich sind.
Das gewünschte Ziel kann auch erreicht werden, indem Sie Ihre eigenen Anmerkungen erstellen. Unter realen Bedingungen ist es normalerweise sinnvoll, eigene Anmerkungen nur dann zu verwenden, wenn Ihre Anforderungen die Funktionen der
integrierten Einschränkungen für den
@Length
Stellen
@Length
diesem Beispiel jedoch vor, dass
@Length
Anmerkungen nicht vorhanden sind. Sie können einen Datenvalidator erstellen, der die Länge einer Zeichenfolge überprüft, indem Sie zwei zusätzliche Klassen erstellen: eine zur Validierung und eine zum Kommentieren von Eigenschaften:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } }
Bitte beachten Sie, dass in diesen Fällen für die korrekte Anwendung des Grundsatzes der Aufgabentrennung die Eigenschaft als gültig markiert werden muss, wenn ihr Wert null ist
(s == null
in der
isValid
Methode), und dann die Annotation
@NotNull
wenn dies für diese Eigenschaft zusätzlich erforderlich ist:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
Häufiger Fehler Nr. 7: Verwenden älterer XML-basierter Konfigurationen
Die Verwendung von XML war eine Notwendigkeit bei der Arbeit mit früheren Versionen von Spring, aber jetzt können die meisten Konfigurationsaufgaben mithilfe von Java-Code und Anmerkungen implementiert werden. XML-Konfigurationen fungieren jetzt als zusätzlicher und optionaler Vorlagencode.
In diesem Artikel (und auch in den zugehörigen GitHub-Repository-Materialien) werden Annotationen zum Konfigurieren von Spring verwendet, und Spring weiß, welche JavaBean-Komponenten gebunden werden müssen, da das Root-Paket mithilfe der zusammengesetzten Annotation @SpringBootApplication mit Annotationen versehen wird - wie folgt:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Diese zusammengesetzte Anmerkung (weitere Informationen hierzu finden Sie in der
Spring-Dokumentation ) teilt der Spring-Plattform lediglich mit, welche Pakete gescannt werden müssen, um die JavaBean-Komponenten zu extrahieren. In unserem speziellen Fall bedeutet dies, dass die folgenden co.kukurin-Unterpakete zum Binden verwendet werden:
- Komponente (TopTalentConverter, MyAnnotationValidator)
- @ RestController (TopTalentController)
- Repository (TopTalentRepository)
- Service- Klassen (TopTalentService)
Wenn wir zusätzliche Klassen mit der Annotation
@Configuration
, werden diese auch auf Java-Konfiguration überprüft.
Häufiger Fehler Nr. 8: Keine Verwendung von Konfigurationsprofilen
Bei der Entwicklung von Serversystemen besteht ein häufiges Problem darin, zwischen verschiedenen Konfigurationen zu wechseln (dies sind in der Regel Konfigurationen für die Betriebs- und Entwicklungsumgebung). Anstatt verschiedene Parameter bei jedem Übergang zwischen Test- und Betriebsmodus manuell zu ändern, ist es effizienter, Konfigurationsprofile zu verwenden.
Stellen Sie sich den Fall vor, wenn Sie in der lokalen Entwicklungsumgebung die Datenbank im RAM verwenden und in der Umgebung des tatsächlichen Betriebs Ihrer Anwendung die MySQL-Datenbank verwendet wird. Dies bedeutet im Wesentlichen, dass Sie unterschiedliche URLs und vermutlich unterschiedliche Anmeldeinformationen verwenden, um auf jede dieser Datenbanken zuzugreifen. Mal sehen, wie dies mit zwei Konfigurationsdateien implementiert werden kann:
DATEI ANWENDUNG.YAML # set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:
DATEI ANWENDUNG-DEV.YAML spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
Es muss davon ausgegangen werden, dass Sie beim Arbeiten mit Code keine zufälligen Aktionen mit einer für die Betriebsumgebung bestimmten Datenbank ausführen möchten. Daher ist es sinnvoll, das Profil für die Entwicklungsumgebung (DEV) als Standardprofil auszuwählen. Anschließend können Sie das Konfigurationsprofil auf dem Server manuell überschreiben, indem Sie
-Dspring.profiles.active=prod
für die JVM
-Dspring.profiles.active=prod
. Darüber hinaus kann das Standardkonfigurationsprofil in der Umgebungsvariablen des Betriebssystems angegeben werden.
Häufiger Fehler Nummer 9. Fehler beim Verwenden des Abhängigkeitsinjektionsmechanismus
Durch die ordnungsgemäße Verwendung des Abhängigkeitsinjektionsmechanismus in Spring kann Spring alle Ihre Objekte binden, indem alle erforderlichen Konfigurationsklassen gescannt werden. Dies ist nützlich, um Abhängigkeiten zu lösen, und erleichtert das Testen erheblich. Anstatt Klassen eng miteinander zu verknüpfen, gilt Folgendes:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
... wir lassen die Spring-Plattform binden:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
In Mishko Heverys Vortrag auf dem Google Tech Talks-Kanal wird ausführlich erklärt, warum die Abhängigkeitsinjektion verwendet werden sollte. Hier sehen wir jedoch, wie dieser Mechanismus in der Praxis angewendet wird. In der Aufteilung der Verantwortung („Common Mistake # 3“) haben wir die Service- und Controller-Klassen erstellt. Angenommen, wir möchten einen Controller testen, vorausgesetzt, die
TopTalentService
Klasse funktioniert ordnungsgemäß. Wir können ein Simulatorobjekt anstelle der aktuellen Service-Implementierung einfügen, indem wir eine separate Konfigurationsklasse erstellen:
@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }
Danach können wir das Simulatorobjekt implementieren,
SampleUnitTestConfig
wir
SampleUnitTestConfig
Spring-Plattform
SampleUnitTestConfig
dass wir
SampleUnitTestConfig
als Konfigurationsquelle verwenden müssen:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
Anschließend können wir die kontextbezogene Konfiguration verwenden, um die benutzerdefinierte JavaBean-Komponente in einen Komponententest einzubetten.
Häufiger Fehler Nummer 10. Fehlende oder falsche Tests
Trotz der Tatsache, dass die Idee des Unit-Tests keineswegs neu ist, scheinen viele Entwickler sie entweder zu "vergessen" (insbesondere wenn sie
nicht obligatorisch sind ) oder sie zu spät auszugeben. Dies ist offensichtlich falsch, da Sie mit den Tests nicht nur die korrekte Funktionsweise des Codes überprüfen können, sondern auch als Dokumentation dienen, die zeigt, wie sich die Anwendung in verschiedenen Situationen verhalten sollte.
Beim Testen von Webdiensten führen Sie selten außergewöhnlich „saubere“ Komponententests durch, da Sie für eine HTTP-Verbindung normalerweise das Spring
DispatcherServlet
Servlet verwenden müssen, um zu sehen, was passiert, wenn Sie eine echte
HttpServletRequest
Anforderung erhalten (
HttpServletRequest
es handelt sich um einen
Integrationstest , der verwendet wird Validierung, Serialisierung usw.). Eine elegante und bewährte Lösung ist die Verwendung von
REST Assured , einer Java-Bibliothek zum bequemen Testen von REST-Diensten mit MockMVC. Betrachten Sie das folgende Codefragment mit Abhängigkeitsinjektion:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception {
SampleUnitTestConfig
bindet die
TopTalentService
der
TopTalentService
Klasse an den
TopTalentController
, und alle anderen Klassen werden unter Verwendung der Standardkonfiguration gebunden, die durch das Scannen von Paketen basierend auf dem Paket der Application-Klasse erhalten wird.
RestAssuredMockMvc
einfach verwendet, um eine
RestAssuredMockMvc
Umgebung einzurichten und eine
GET
Anforderung an den
/toptal/get
senden.
Verwenden Sie Spring professionell
Spring ist eine leistungsstarke Plattform, mit der man leicht anfangen kann, die jedoch Zeit und Mühe benötigt, um sie vollständig zu beherrschen. Wenn Sie sich die Zeit nehmen, sich mit dieser Plattform vertraut zu machen, wird dies zweifellos die Effizienz Ihrer Arbeit steigern, Ihnen helfen, saubereren Code zu erstellen und Ihre Qualifikationen als Entwickler zu verbessern.
Ich empfehle Ihnen, auf
Spring In Action zu achten - dies ist ein gutes, anwendungsorientiertes Buch, in dem viele wichtige Themen im Zusammenhang mit der Spring-Plattform behandelt werden.
Zu diesem Zeitpunkt wurde die Übersetzung dieses Artikels beendet.
Lesen Sie den ersten Teil .