Jak zrobić aplikację REST-ową w Spring Boot

Jak zrobić aplikację REST-ową w Spring Boot

 

O czym jest ten artykuł?

Moim celem jest przedstawić framework Spring Boot w sposób przystępny dla osób bez wcześniejszego doświadczenia w pisaniu aplikacji internetowych, w stopniu wystarczającym do wytworzenia czegoś działającego (powiedzmy, że jest to "crash course" do Spring Boota). Nie chcę jednak zanudzać czytelnika parafrazowaniem dokumentacji, również nie chcę pisać klonu wstępu do frameworku dostępnego na oficjalnej stronie. Dlatego skupię się na kluczowych dla mnie elementach o zasadzie działania frameworku, o których sam chciałbym wiedzieć wcześniej zanim spędziłem dziesiątki godzin gapiąc się na błędy.
 

Zakładam znajomość czytelnika z zasadą działania komunikacji klient-serwer, oraz podstawową znajomość języka Java, to znaczy składnia oraz pojęcie o programowaniu obiektowym. W artykule w kwestiach zależności projektu wykorzystany jest Maven, ale nie jest to żadna fizyka jądrowa, więc nie trzeba go wcześniej znać.

Spring Springowi nie równy

Warto jest najpierw powiedzieć... czym w sumie różni się Spring od Spring Boot, bo biorąc się za pisanie pierwszego projektu nie jest to takie oczywiste. Spring Boot to rozszerzenie/nakładka na czystego Springa, obydwa to są w pełni funkcjonalne frameworki nadające się do tworzenia aplikacji, nawet nie internetowych, choć to jest zdecydowanie najpopularniejsze zastosowanie. Żeby było ciekawiej, potocznie na Spring Boot i tak mówi się Spring.

"Boot" w nazwie nawiązuje do szybkiej możliwości postawienia aplikacji na nogi, bez skomplikowanej konfiguracji wymaganej przez czystego Springa. Konkretniej, musielibyśmy instruować Springa gdzie i jak ma szukać naszych klas "komponentów" (o tym więcej dalej), ręcznie zdefiniować embedowany serwer, który uruchamia naszą aplikację, lub proces deployowania naszej aplikacji do .war dla osobno stojącego serwera. Dodatkowo trzeba konfigurować inne komponenty które są nam potrzebne, na przykład silnik szablonów do stron (np. Thymeleaf) czy komponent bezpieczeństwa.

Wszystkie te rzeczy są załatwione w Spring Boot poprzez wybranie odpowiednich zależności w pom.xml. Koniec. Generalnie nie ma sensu tworzyć nowych projektów w czystym Springu jeżeli nie ma takiej potrzeby.

Jak działa Spring

Ta część artykułu dotyczy samego Springa, ponieważ korzystanie ze Spring Boota nie zwalnia z potrzeby świadomości o tym, jak to wszystko działa. Co więcej, przez to że nie konfigurujemy niczego ręcznie, łatwiej jest wpaść na bezsensowne błędy, bo można zajść dość daleko w pisaniu działającej aplikacji, kiedy nie wiemy co się faktycznie dzieje pod spodem.

Prawdopodobnie najważniejszą cechą Springa jest Inversion of Control (IoC), czyli podejście polegające na tworzeniu kawałków kodu, które będą wywoływane półautomatycznie przez framework. "Odwracanie" oznacza tutaj odwrotne podejście w porównaniu do tradycyjnych bibliotek z imperatywnych podejść, gdzie to użytkownik zawsze importuje bibliotekę, a następnie korzysta z jej funkcji.

Na przykład, w ten sposób wygląda kod odpowiedzialny za odpowiedź na zapytanie do głównej strony:

@RestController
public class HelloController {

	@GetMapping("/")
	public String index() {
		return "Greetings from Spring Boot!";
	}

}
Przykład z https://spring.io/guides/gs/spring-boot/

Edytor kodu bez większej integracji ze Springiem mógłby zacząć krzyczeć, że metoda index() nie jest nigdzie wykorzystana, co się zgadza, ponieważ nigdy sami jej nie wywołujemy. Anotacje @RestController i @GetMapping są odpowiedzialne za to, że aplikacja zaczyna coś robić, to są tylko dwie z obszernego zestawu anotacji, na których opiera się Spring.

Działanie Springa opiera się o komponenty, czyli podstawowe klocki budujące aplikację, których życiem Spring zajmuje się automatycznie. Podczas działania aplikacji utrzymywany jest tak zwany kontekst aplikacji, w którym przetrzymywane są instancje tych klas (nazywane są jako "Bean"), Kiedy oznaczymy klasę anotacją @Component, lub jej pochodną, Spring znajdzie tą klasę podczas inicjalizacji i potraktuje ją jako komponent, to znaczy stworzy i przechowa w konekście aplikacji tylko jedną instancję (pomijając szczegóły, jes to wzorzec singleton). Komponenty mogą wymagać innych komponentów, które Spring automatycznie udostępni.

 
Kilka przykładowych komponentów i kontekst z Beanami

Widoczna wyżej anotacja @RestController jest właśnie pochodną @Component i jest automatycznie tworzona, oraz ma dodatkowe funkcje do tworzenia aplikacji REST.

Spring korzysta z domyślnego konstruktora klasy do inicjalizacji komponentów. Jeżeli w naszym kodzie próbujemy gdzieś robić to samo ręcznie, to najprawdopodobniej robimy coś źle, komponenty powinny same się "pojawić" w polach klasy o odopwiednim typie. Takie podejście nazywa się Dependency Injection, zależne klasy są wstrzykiwane przez Spring. Jeżeli Spring nie jest w stanie znaleźć takiego komponentu w kontekście aplikacji (na przykład klasa nie została oznaczona jako komponent), to dostajemy błąd UnsatisfiedDependencyException.

Stworzenie projektu

Tworzenie dowolnego projektu w javie, nie tylko do Spring Boota, zaczynia się od stworzenia instrukcji budowania, ponieważ nie chcemy uruchamiać kompilatora ręcznie z linii komend. Każde narzędzie do budowania robi to trochę inaczej, w Mavenie jest to plik pom.xml w najwyższym katalogu. Oczywiście nikt nie będzie pisał tego ręcznie, jest od tego strona https://start.spring.io/, nowoczesne IDE również mają wbudowany analogiczny generator takich projektów.

 
Generator projektów

Lista dostępnych zależności może przyprawić o ból głowy, ponieważ mniej i bardziej zaawansowane pozycje są na jednej wielkiej liście. Do podstawowych projektów potrzebny jest tylko "Spring Web", gdy chcemy korzystać z bazy danych musimy też znaleźć odpowiednią pozycję "Spring data". Otrzymujemy projekt w archiwum .zip:


 

Plik Application.java posiada krótki kawałek kodu uruchamiający aplikację

package com.example.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

}

 Anotacja @SpringBootApplication składa się z kilku innych, mniej i bardziej ważnych:

@EnableAutoConfiguration zajmuje się wcześniej wspomnianą konfiguracją, @ComponentScan uruchamia poszukiwanie komponentów. Bardzo ważnym faktem jest to, że skanowanie zaczyna się od obecnej paczki, i schodzi tylko w dół hierarchii. Dlatego złym pomysłem jest zmienianie gotowej struktury z generatora, ponieważ można przez przypadek źle umieścić klasę komponentu i Spring nie będzie w stanie jej znaleźć. Proponowanym pomysłem jest stworzenie dalszych paczek dla różnych typów komponentów:

 

Lub podzielenie na paczki odpowiedzialne za konkretne części aplikacji.

Warto jest spojrzeć na różne przykładowe aplikacje z otwartym źródłem, kilka przykładów: 1.https://github.com/spring-attic/sagan, 2.https://github.com/spring-projects/spring-petclinic

Po co to wszystko?

Można mieć wrażenie, że mimo obiecywanych uproszczeń, Spring Boot i tak wydaje się być strzelaniem z armaty do muchy, kiedy jedyne co chcemy to prosty kod do obsługi żądań. Porównując inne języki programowania i ich frameworki (Django, nodejs+express) faktycznie dłużej schodzi się za pierwszym razem, jednak na dłuższą metę Spring Boot jest dobrym wyborem. Spring Boot wymaga trochę więcej cierpliwości i jest przeznaczony do większych projektów, jednak zaletą jest zmuszanie programisty do pisania testowalnego kodu, ponieważ wymuszona jest separacja komponentów (gdzie w małych projektach prawdopodobnie obyłoby się bez testów).



 


Komentarze

Popularne posty z tego bloga

Wprowadzenie do Reacta

Coś o Cassandrze