10 najczęstszych błędów Spring Framework
Opublikowany: 2022-03-11Spring jest prawdopodobnie jednym z najpopularniejszych frameworków Java, a także potężną bestią do oswojenia. Chociaż jego podstawowe koncepcje są dość łatwe do zrozumienia, stanie się silnym programistą Springa wymaga trochę czasu i wysiłku.
W tym artykule omówimy niektóre z najczęstszych błędów w Springu, szczególnie zorientowanych na aplikacje internetowe i Spring Boot. Jak napisano na stronie internetowej Spring Boot, Spring Boot ma uparty pogląd na temat tego, jak należy budować aplikacje gotowe do produkcji, więc w tym artykule spróbujemy naśladować ten pogląd i przedstawimy przegląd kilku wskazówek, które będą dobrze integrować się z tworzeniem standardowych aplikacji internetowych Spring Boot.
Jeśli nie znasz Spring Boot, ale nadal chciałbyś wypróbować niektóre z wymienionych rzeczy, stworzyłem repozytorium GitHub towarzyszące temu artykułowi. Jeśli czujesz się zagubiony w dowolnym momencie artykułu, polecam sklonowanie repozytorium i zabawę z kodem na lokalnym komputerze.
Powszechny błąd nr 1: schodzenie na zbyt niski poziom
Uderza nas tym powszechnym błędem, ponieważ syndrom „nie wymyślono tutaj” jest dość powszechny w świecie programistycznym. Objawy obejmują regularne przepisywanie fragmentów powszechnie używanego kodu i wydaje się, że cierpi z tego powodu wielu programistów.
Chociaż zrozumienie elementów wewnętrznych konkretnej biblioteki i jej implementacji jest w większości dobre i konieczne (i może być również świetnym procesem uczenia się), to szkodliwe dla twojego rozwoju jako inżyniera oprogramowania jest ciągłe zajmowanie się tą samą niskopoziomową implementacją Detale. Istnieje powód, dla którego istnieją abstrakcje i frameworki, takie jak Spring, który jest właśnie po to, aby oddzielić Cię od powtarzalnej pracy ręcznej i pozwolić Ci skoncentrować się na szczegółach wyższego poziomu – obiektach domeny i logice biznesowej.
Więc ogarnij abstrakcje - następnym razem, gdy napotkasz konkretny problem, najpierw wykonaj szybkie wyszukiwanie i ustal, czy biblioteka rozwiązująca ten problem jest już zintegrowana ze Springiem; w dzisiejszych czasach są szanse, że znajdziesz odpowiednie istniejące rozwiązanie. Jako przykład użytecznej biblioteki będę używał adnotacji Project Lombok w przykładach w dalszej części tego artykułu. Lombok jest używany jako generator kodu wzorcowego i miejmy nadzieję, że leniwy programista w tobie nie powinien mieć problemu z zapoznaniem się z biblioteką. Jako przykład sprawdź, jak wygląda „standardowa fasola Java” z Lombok:
@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; }
Jak możesz sobie wyobrazić, powyższy kod kompiluje się do:
public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } }
Pamiętaj jednak, że najprawdopodobniej będziesz musiał zainstalować wtyczkę, jeśli zamierzasz używać Lombok ze swoim IDE. Wersję wtyczki IntelliJ IDEA można znaleźć tutaj.
Powszechny błąd nr 2: „Wyciek” elementów wewnętrznych
Ujawnienie struktury wewnętrznej nigdy nie jest dobrym pomysłem, ponieważ stwarza brak elastyczności w projektowaniu usług, a w konsekwencji promuje złe praktyki kodowania. „Wyciekające” elementy wewnętrzne objawiają się udostępnianiem struktury bazy danych z określonych punktów końcowych API. Jako przykład załóżmy, że następujący POJO („Zwykły stary obiekt Java”) reprezentuje tabelę w Twojej bazie danych:
@Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } }
Załóżmy, że istnieje punkt końcowy, który musi uzyskać dostęp do danych TopTalentEntity
. Choć kuszące może być zwracanie instancji TopTalentEntity
, bardziej elastycznym rozwiązaniem byłoby utworzenie nowej klasy reprezentującej dane TopTalentEntity
na punkcie końcowym API:
@AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; }
W ten sposób wprowadzanie zmian w zapleczu bazy danych nie będzie wymagało żadnych dodatkowych zmian w warstwie usług. Zastanów się, co by się stało w przypadku dodania pola „hasło” do TopTalentEntity
w celu przechowywania skrótów haseł użytkowników w bazie danych — bez łącznika, takiego jak TopTalentData
, zapomnienie o zmianie frontonu usługi mogłoby przypadkowo ujawnić bardzo niepożądane tajne informacje !
Powszechny błąd nr 3: brak rozdzielenia obaw
Wraz z rozwojem aplikacji organizacja kodu staje się coraz ważniejszą sprawą. Jak na ironię, większość dobrych zasad inżynierii oprogramowania zaczyna się załamywać na dużą skalę – zwłaszcza w przypadkach, w których nie poświęcono zbyt wiele uwagi projektowaniu architektury aplikacji. Jednym z najczęstszych błędów, na które zwykle ulegają programiści, jest mieszanie problemów związanych z kodem, a jest to niezwykle łatwe do zrobienia!
To, co zwykle łamie separację obaw, to po prostu „zrzucanie” nowej funkcjonalności do istniejących klas. Jest to oczywiście świetne rozwiązanie krótkoterminowe (na początek wymaga mniej pisania), ale nieuchronnie staje się problemem w dalszej części drogi, czy to podczas testów, konserwacji, czy gdzieś pomiędzy. Rozważmy następujący kontroler, który zwraca TopTalentData
ze swojego repozytorium:
@RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping("/toptal/get") public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
Na pierwszy rzut oka może się wydawać, że z tym fragmentem kodu nie jest coś szczególnie złego; udostępnia listę TopTalentData
, która jest pobierana z instancji TopTalentEntity
. Przyglądając się jednak bliżej, widzimy, że w rzeczywistości jest kilka rzeczy, które wykonuje tutaj TopTalentController
; mianowicie jest mapowaniem żądań do określonego punktu końcowego, pobieraniem danych z repozytorium i konwertowaniem jednostek otrzymanych z TopTalentRepository
na inny format. „Czystszym” rozwiązaniem byłoby podzielenie tych obaw na ich własne klasy. Może to wyglądać mniej więcej tak:
@RestController @RequestMapping("/toptal") @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping("/get") public List<TopTalentData> getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } }
Dodatkową zaletą tej hierarchii jest to, że pozwala nam określić, gdzie znajduje się funkcjonalność, po prostu sprawdzając nazwę klasy. Co więcej, podczas testowania możemy łatwo zastąpić dowolną z klas fikcyjną implementacją, jeśli zajdzie taka potrzeba.
Powszechny błąd nr 4: niespójność i słaba obsługa błędów
Temat spójności niekoniecznie dotyczy wyłącznie Springa (lub Javy), ale nadal jest ważnym aspektem, który należy wziąć pod uwagę podczas pracy nad projektami Spring. Chociaż styl kodowania może być przedmiotem dyskusji (i zwykle jest to kwestia porozumienia w zespole lub w całej firmie), posiadanie wspólnego standardu okazuje się bardzo pomocne. Dotyczy to zwłaszcza zespołów wieloosobowych; spójność umożliwia przekazanie bez poświęcania wielu zasobów na trzymanie za rękę lub dostarczanie długich wyjaśnień dotyczących obowiązków różnych klas
Rozważ projekt Spring z różnymi plikami konfiguracyjnymi, usługami i kontrolerami. Spójność semantyczna w ich nazewnictwie tworzy łatwo przeszukiwalną strukturę, w której każdy nowy programista może zarządzać swoim sposobem poruszania się po kodzie; na przykład dołączanie sufiksów Config do klas konfiguracyjnych, sufiksów usług do usług i sufiksów kontrolerów do kontrolerów.
Ściśle związana z tematem spójności, na szczególne podkreślenie zasługuje obsługa błędów po stronie serwera. Jeśli kiedykolwiek miałeś do czynienia z odpowiedziami na wyjątki ze źle napisanego interfejsu API, prawdopodobnie wiesz, dlaczego — prawidłowe przeanalizowanie wyjątków może być uciążliwe, a jeszcze bardziej bolesne może być określenie przyczyny wystąpienia tych wyjątków w pierwszej kolejności.
Jako programista API najlepiej byłoby objąć wszystkie punkty końcowe skierowane do użytkowników i przetłumaczyć je na typowy format błędów. Zwykle oznacza to posiadanie ogólnego kodu błędu i opisu, a nie rozwiązanie polegające na a) zwróceniu komunikatu „500 Internal Server Error” lub b) po prostu zrzuceniu śladu stosu do użytkownika (czego właściwie należy unikać za wszelką cenę ponieważ odsłania twoje wewnętrzne elementy oprócz trudności w obsłudze po stronie klienta).
Przykładem typowego formatu odpowiedzi na błąd może być:
@Value public class ErrorResponse { private Integer errorCode; private String errorMessage; }
Coś podobnego do tego jest powszechnie spotykane w większości popularnych interfejsów API i zwykle działa dobrze, ponieważ można je łatwo i systematycznie dokumentować. Tłumaczenie wyjątków na ten format można wykonać, dostarczając adnotację @ExceptionHandler
do metody (przykład adnotacji znajduje się w Common Mistake #6).
Powszechny błąd nr 5: niewłaściwa obsługa wielowątkowości
Niezależnie od tego, czy występuje w aplikacjach komputerowych, internetowych, Spring czy bez Spring, wielowątkowość może być trudnym orzechem do zgryzienia. Problemy spowodowane równoległym wykonywaniem programów są niesamowicie nieuchwytne i często bardzo trudne do debugowania — w rzeczywistości, ze względu na naturę problemu, gdy zdasz sobie sprawę, że masz do czynienia z problemem związanym z równoległym wykonywaniem, prawdopodobnie musisz całkowicie zrezygnować z debugera i sprawdzić swój kod „ręcznie”, aż znajdziesz główną przyczynę błędu. Niestety, rozwiązanie do wycinania ciasteczek nie istnieje, aby rozwiązać takie problemy; w zależności od konkretnego przypadku będziesz musiał ocenić sytuację, a następnie zaatakować problem pod kątem, który uważasz za najlepszy.
Najlepiej byłoby oczywiście całkowicie uniknąć błędów związanych z wielowątkowością. Ponownie, nie istnieje podejście uniwersalne, ale oto kilka praktycznych rozważań dotyczących debugowania i zapobiegania błędom wielowątkowym:
Unikaj globalnego stanu
Po pierwsze, zawsze pamiętaj o kwestii „państwa globalnego”. Jeśli tworzysz aplikację wielowątkową, absolutnie wszystko, co można modyfikować globalnie, powinno być ściśle monitorowane i, jeśli to możliwe, całkowicie usunięte. Jeśli istnieje powód, dla którego zmienna globalna musi pozostać modyfikowalna, ostrożnie zastosuj synchronizację i śledź wydajność aplikacji, aby upewnić się, że nie jest powolna z powodu nowo wprowadzonych okresów oczekiwania.
Unikaj zmienności
Ten pochodzi bezpośrednio z programowania funkcjonalnego i, dostosowany do OOP, stwierdza, że należy unikać zmienności klas i zmiany stanu. Krótko mówiąc, oznacza to rezygnację z metod ustawiających i posiadanie prywatnych pól końcowych we wszystkich klasach modeli. Ich wartości są mutowane tylko podczas budowy. W ten sposób możesz mieć pewność, że nie pojawią się żadne problemy z rywalizacją i że dostęp do właściwości obiektu zawsze zapewni prawidłowe wartości.
Rejestruj kluczowe dane
Oceń, gdzie Twoja aplikacja może powodować problemy, i prewencyjnie rejestruj wszystkie kluczowe dane. Jeśli wystąpi błąd, będziesz wdzięczny za informację, które wnioski zostały otrzymane, i będziesz mieć lepszy wgląd w to, dlaczego Twoja aplikacja źle się zachowywała. Ponownie należy zauważyć, że rejestrowanie wprowadza dodatkowe operacje we/wy plików i dlatego nie powinno być nadużywane, ponieważ może poważnie wpłynąć na wydajność aplikacji.

Ponowne wykorzystanie istniejących implementacji
Zawsze, gdy potrzebujesz stworzyć własne wątki (np. do tworzenia żądań asynchronicznych do różnych usług), użyj ponownie istniejących bezpiecznych implementacji, zamiast tworzyć własne rozwiązania. W większości będzie to oznaczać wykorzystanie usług ExecutorServices i zgrabnych, funkcjonalnych CompletableFutures Java 8 do tworzenia wątków. Spring umożliwia również asynchroniczne przetwarzanie żądań za pośrednictwem klasy DeferredResult.
Powszechny błąd nr 6: niestosowanie walidacji opartej na adnotacjach
Wyobraźmy sobie, że nasza wcześniejsza usługa TopTalent wymaga punktu końcowego do dodawania nowych Top Talenty. Co więcej, powiedzmy, że z jakiegoś naprawdę ważnego powodu każda nowa nazwa musi mieć dokładnie 10 znaków. Jednym ze sposobów na zrobienie tego może być:
@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) { // throw some exception } topTalentService.addTopTalent(topTalentData); }
Jednak powyższe (oprócz tego, że jest źle skonstruowane) nie jest tak naprawdę „czystym” rozwiązaniem. Sprawdzamy więcej niż jeden typ ważności (a mianowicie, czy TopTalentData
nie ma wartości null, czy TopTalentData.name
nie ma wartości null oraz czy TopTalentData.name
ma 10 znaków), a także zgłaszamy wyjątek, jeśli dane są nieprawidłowe .
Można to zrobić znacznie czyściej, używając walidatora Hibernate ze Springiem. Najpierw zrefaktoryzujmy metodę addTopTalent
w celu obsługi walidacji:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { // handle validation exception }
Dodatkowo będziemy musieli wskazać jaką właściwość chcemy walidować w klasie TopTalentData
:
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
Teraz Spring przechwyci żądanie i zweryfikuje je przed wywołaniem metody – nie ma potrzeby stosowania dodatkowych testów ręcznych.
Innym sposobem, w jaki moglibyśmy osiągnąć to samo, jest tworzenie własnych adnotacji. Chociaż zwykle stosujesz niestandardowe adnotacje tylko wtedy, gdy Twoje potrzeby przekraczają wbudowany zestaw ograniczeń Hibernate, w tym przykładzie załóżmy, że @Length nie istnieje. Zrobiłbyś walidator, który sprawdza długość łańcucha, tworząc dwie dodatkowe klasy, jedną do walidacji, a drugą do adnotacji właściwości:
@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; } }
Należy pamiętać, że w takich przypadkach najlepsze praktyki w zakresie oddzielania problemów wymagają oznaczenia właściwości jako prawidłowej, jeśli ma ona wartość null ( s == null
w ramach metody isValid
), a następnie użycia adnotacji @NotNull
, jeśli jest to dodatkowe wymaganie dla własność:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
Powszechny błąd nr 7: (Nadal) używanie konfiguracji opartej na XML
Podczas gdy XML był niezbędny w poprzednich wersjach Springa, obecnie większość konfiguracji można wykonać wyłącznie za pomocą kodu / adnotacji Java; Konfiguracje XML stanowią tylko dodatkowy i niepotrzebny kod wzorcowy.
Ten artykuł (jak również towarzyszące mu repozytorium GitHub) używa adnotacji do konfigurowania Spring i Spring wie, które ziarna powinny być połączone, ponieważ pakiet główny został opatrzony adnotacją @SpringBootApplication
, na przykład:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Adnotacja złożona (możesz dowiedzieć się więcej w dokumentacji Springa po prostu daje Springowi wskazówkę, które pakiety należy przeskanować, aby pobrać ziarna. W naszym konkretnym przypadku oznacza to, że zostanie użyty następujący pakiet pod wierzchem (co.kukurin) do okablowania:
-
@Component
(TopTalentConverter
,MyAnnotationValidator
) -
@RestController
(TopTalentController
) -
@Repository
(TopTalentRepository
) -
@Service
(TopTalentService
)
Gdybyśmy mieli jakieś dodatkowe klasy z adnotacjami @Configuration
, zostałyby one również sprawdzone pod kątem konfiguracji opartej na Javie.
Powszechny błąd nr 8: Zapominanie o profilach
Problemem często napotykanym podczas tworzenia serwerów jest rozróżnianie różnych typów konfiguracji, zwykle konfiguracji produkcyjnej i programistycznej. Zamiast ręcznie zastępować różne wpisy konfiguracji za każdym razem, gdy przechodzisz z testowania do wdrażania aplikacji, bardziej wydajnym sposobem byłoby zastosowanie profili.
Rozważ przypadek, w którym używasz bazy danych w pamięci do programowania lokalnego, z bazą danych MySQL w środowisku produkcyjnym. Zasadniczo oznaczałoby to, że będziesz używać innego adresu URL i (miejmy nadzieję) różnych danych uwierzytelniających, aby uzyskać dostęp do każdego z nich. Zobaczmy, jak można to zrobić w dwóch różnych plikach konfiguracyjnych:
plik application.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:
plik application-dev.yaml
spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
Przypuszczalnie nie chciałbyś przypadkowo wykonywać żadnych działań na produkcyjnej bazie danych podczas majstrowania przy kodzie, więc warto ustawić domyślny profil na dev. Na serwerze można następnie ręcznie przesłonić profil konfiguracyjny, podając parametr -Dspring.profiles.active=prod
do maszyny JVM. Alternatywnie możesz również ustawić zmienną środowiskową swojego systemu operacyjnego na żądany profil domyślny.
Powszechny błąd nr 9: nieprzyjęcie wstrzykiwania zależności
Właściwe użycie wstrzykiwania zależności ze Springiem oznacza umożliwienie połączenia wszystkich obiektów razem poprzez skanowanie wszystkich pożądanych klas konfiguracyjnych; okazuje się to przydatne do rozdzielenia relacji, a także znacznie ułatwia testowanie. Zamiast mocno sprzęgać klasy, robiąc coś takiego:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
Pozwalamy Spring wykonać okablowanie za nas:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
Wypowiedź Misko Hevery'ego w Google szczegółowo wyjaśnia „dlaczego” wstrzykiwanie zależności, więc zamiast tego zobaczmy, jak to jest wykorzystywane w praktyce. W sekcji dotyczącej separacji obaw (Common Mistakes #3) stworzyliśmy klasę usługi i kontrolera. Powiedzmy, że chcemy przetestować kontroler przy założeniu, że TopTalentService
zachowuje się poprawnie. Możemy wstawić atrapę obiektu w miejsce rzeczywistej implementacji usługi, dostarczając osobną klasę konfiguracyjną:
@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; } }
Następnie możemy wstrzyknąć obiekt próbny, mówiąc Springowi, aby użył SampleUnitTestConfig
jako dostawcy konfiguracji:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
To z kolei pozwala nam użyć konfiguracji kontekstu do wstrzyknięcia niestandardowego ziarna do testu jednostkowego.
Powszechny błąd nr 10: brak lub niewłaściwe testowanie
Mimo że idea testów jednostkowych jest z nami od dawna, wielu programistów wydaje się albo „zapominać” o tym (zwłaszcza jeśli nie jest to wymagane ), albo po prostu dodaje to po namyśle. Nie jest to oczywiście pożądane, ponieważ testy powinny nie tylko weryfikować poprawność kodu, ale także służyć jako dokumentacja tego, jak aplikacja powinna zachowywać się w różnych sytuacjach.
Testując usługi sieciowe, rzadko wykonujesz wyłącznie „czyste” testy jednostkowe, ponieważ komunikacja przez HTTP zwykle wymaga wywołania DispatcherServlet
Springa i zobaczenia, co się stanie, gdy zostanie odebrany rzeczywisty HttpServletRequest
(co czyni go testem integracyjnym , zajmuje się walidacją, serializacją itp.). REST Assured, Java DSL do łatwego testowania usług REST, jako dodatek do MockMVC, okazała się być bardzo eleganckim rozwiązaniem. Rozważmy następujący fragment kodu z wstrzykiwaniem zależności:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get"); // then response.then().statusCode(200); response.then().body("name", hasItems("Mary", "Joel")); } }
SampleUnitTestConfig
implementację TopTalentService
z TopTalentController
, podczas gdy wszystkie inne klasy są okablowane przy użyciu standardowej konfiguracji wywnioskowanej z pakietów skanowania zakorzenionych w pakiecie klasy aplikacji. RestAssuredMockMvc
służy po prostu do konfigurowania lekkiego środowiska i wysyłania żądania GET
do punktu końcowego /toptal/get
.
Zostań mistrzem wiosny
Spring to potężny framework, z którym łatwo zacząć, ale wymaga pewnego poświęcenia i czasu, aby osiągnąć pełne mistrzostwo. Poświęcenie czasu na zapoznanie się z frameworkiem zdecydowanie poprawi Twoją produktywność na dłuższą metę i ostatecznie pomoże Ci napisać czystszy kod i stać się lepszym programistą.
Jeśli szukasz dalszych zasobów, Spring In Action to dobra praktyczna książka obejmująca wiele podstawowych tematów Spring.