
Jeśli uczysz się programowania w Javie, prędzej czy później natrafisz na dwa słowa, które padają niemal w każdej ofercie pracy: Spring i Spring Boot. Brzmią podobnie, często występują razem, a mimo to nie znaczą tego samego. W tym przewodniku wyjaśnimy od zera, co to jest Spring Framework, co to jest Spring Boot i czym te dwie rzeczy się różnią. Nie na diagramach z internetu, tylko na prawdziwej aplikacji, którą specjalnie na potrzeby tego artykułu zbudowaliśmy i uruchomiliśmy: mała księgarnia z REST-owym adresem (interfejsem programistycznym zwracającym dane) i strona HTML renderowana na serwerze. Wszystkie zrzuty pochodzą z tej działającej aplikacji.
W pigułce: Spring Framework to szkielet (framework) do budowy aplikacji w Javie - daje mechanizmy takie jak wstrzykiwanie zależności, obsługa żądań sieciowych czy transakcje. Spring Boot to nadbudowa nad Spring Framework, która te mechanizmy sama konfiguruje i pozwala uruchomić aplikację jednym poleceniem. Krótko: Framework daje klocki, a Boot składa je za Ciebie i od razu uruchamia.
Z tego artykułu dowiesz się:
- co to jest Spring Framework i z jakich modułów się składa,
- czym jest wstrzykiwanie zależności (dependency injection) i po co komu kontener IoC,
- co to jest Spring Boot i czym różni się od samego Spring Framework,
- czym są startery i na czym polega auto-konfiguracja,
- jak wygenerować projekt na Spring Initializr i uruchomić go jednym poleceniem,
- jak Spring obsługuje żądanie WWW (Spring MVC) i jak zwraca dane jako JSON,
- jak renderować strony HTML na serwerze za pomocą Thymeleaf,
- do czego służy Actuator i jak sprawdzić kondycję aplikacji,
- jak wygląda cały ekosystem Spring.
Wolisz od razu zajrzeć w działający kod? Cały projekt tej aplikacji (Spring Boot, Maven, Java 21) jest publicznie na GitHubie. Sklonuj go i uruchom u siebie jednym poleceniem, żeby przejść ten przewodnik krok po kroku na własnym komputerze - wszystkie zrzuty w artykule pochodzą dokładnie z tego projektu:
git clone https://github.com/konwertyta/spring-boot-bookstore-demo.git
cd spring-boot-bookstore-demo
mvn spring-boot:run
Co to jest Spring Framework?
Spring Framework to szkielet do budowy aplikacji w języku Java, który bierze na siebie techniczną czarną robotę, żebyś Ty mógł skupić się na logice biznesowej. Słowo szkielet (z angielskiego framework) oznacza gotowy fundament aplikacji: zestaw mechanizmów i zasad, w które wpinasz swój kod, zamiast pisać wszystko od podstaw. Spring zajmuje się między innymi tworzeniem obiektów i łączeniem ich ze sobą, obsługą żądań przychodzących z sieci, dostępem do bazy danych czy transakcjami (czyli pilnowaniem, żeby zestaw operacji na bazie wykonał się w całości albo wcale).
Kluczowe jest to, że Spring jest modularny. Nie jest jednym wielkim blokiem, który musisz wziąć w całości. To zestaw współpracujących modułów, z których bierzesz tylko te, których potrzebujesz. Sercem całości jest tak zwany kontener (o nim za chwilę), a wokół niego narastają moduły do sieci, danych czy testów.

Krótka historia Springa
Spring powstał jako odpowiedź na przekombinowanie wczesnych technologii Javy korporacyjnej. Jego korzenie sięgają kodu, który Rod Johnson opublikował w 2002 roku, a pierwsza wersja frameworka ukazała się w 2004. Chodziło o jedno: żeby budowa aplikacji była prosta, a zwykły obiekt Javy (tak zwany POJO, czyli Plain Old Java Object - obiekt bez narzuconych zależności od frameworka) nie musiał dziedziczyć po dziwnych klasach ani wypełniać stert konfiguracji. Przez ponad dwie dekady Spring urósł do najpopularniejszego szkieletu w świecie Javy po stronie serwera.
Wstrzykiwanie zależności, czyli serce Springa
Żeby zrozumieć Springa, trzeba zrozumieć jeden pomysł: wstrzykiwanie zależności (z angielskiego dependency injection). Brzmi poważnie, a chodzi o coś prostego. Wyobraź sobie, że kontroler obsługujący żądania potrzebuje serwisu z logiką. Bez Springa musiałbyś sam napisać new BookstoreService() w każdym miejscu, w którym ten serwis jest potrzebny, i sam pilnować, ile takich obiektów powstaje. Ze Springiem jest odwrotnie: to Spring tworzy obiekt i sam podaje go tam, gdzie jest potrzebny. Ty tylko mówisz, czego potrzebujesz.
To odwrócenie odpowiedzialności nazywa się odwróceniem sterowania (z angielskiego Inversion of Control, w skrócie IoC). Nie Ty tworzysz i spinasz obiekty, tylko robi to za Ciebie kontener Springa - to znaczy ta część frameworka, która zarządza cyklem życia obiektów. Same obiekty tworzone i zarządzane przez kontener nazywamy ziarnami (z angielskiego beans). Zobaczmy to na animacji z naszej księgarni:

W naszej aplikacji serwis to zwykła klasa oznaczona adnotacją (czyli specjalnym znacznikiem) @Service. Adnotacja mówi Springowi: to jest komponent, zajmij się nim. Dzięki temu Spring sam utworzy jego obiekt i będzie go pilnował.
@Service
public class BookstoreService {
private final List<Book> catalog = List.of(
new Book(1L, "Thinking in Java", "Bruce Eckel", 2006, new BigDecimal("149.00"), "Java"),
new Book(2L, "Spring in Action", "Craig Walls", 2022, new BigDecimal("189.00"), "Spring"));
public List<Book> findAll() {
return catalog;
}
public List<Book> findByCategory(String category) {
return catalog.stream()
.filter(b -> b.category().equalsIgnoreCase(category))
.toList();
}
}
A teraz najważniejsze. Kontroler nie tworzy serwisu ręcznie. On tylko przyjmuje go w konstruktorze, a Spring sam podstawia gotowy obiekt. To właśnie jest wstrzykiwanie zależności przez konstruktor - dziś zalecany sposób w Springu:
@RestController
@RequestMapping("/api/books")
public class BookRestController {
private final BookstoreService service;
// Nie piszemy 'new BookstoreService()'. Spring sam podaje gotowy
// obiekt serwisu przez konstruktor - to jest wstrzykiwanie zależności.
public BookRestController(BookstoreService service) {
this.service = service;
}
@GetMapping
public List<Book> list(@RequestParam(required = false) String category) {
return category == null ? service.findAll() : service.findByCategory(category);
}
}
Dlaczego to takie ważne? Bo dzięki temu klasy nie są ze sobą na sztywno zrośnięte. Serwis można podmienić (na przykład na wersję testową) bez dotykania kontrolera, a cały kod łatwiej się testuje i utrzymuje. To jest realna wartość Springa, a nie sama adnotacja.
Co to jest Spring Boot i czym różni się od Spring Framework?
Sam Spring Framework jest potężny, ale na starcie bywa wymagający: trzeba samodzielnie poskładać konfigurację, wybrać i podpiąć serwer, dobrać pasujące do siebie wersje bibliotek. W 2014 roku pojawił się Spring Boot, który tę barierę zdejmuje. Spring Boot to nadbudowa nad Spring Framework, która sama dobiera rozsądną konfigurację, ma wbudowany serwer WWW i pozwala uruchomić aplikację jednym poleceniem.
Kluczowe słowo to nadbudowa, a nie zamiennik. Spring Boot nie zastępuje Spring Framework - on na nim stoi i go wykorzystuje. Kiedy piszesz aplikację w Spring Boot, cały czas korzystasz z mechanizmów Spring Framework (choćby z opisanego wyżej wstrzykiwania zależności). Boot jedynie oszczędza Ci ręcznej roboty konfiguracyjnej.

Startery: koniec z ręcznym dobieraniem bibliotek
Pierwsza wygoda Spring Boot to startery. Starter to jedna zależność, która pod spodem wciąga cały zestaw dobranych i przetestowanych razem bibliotek. Zamiast ręcznie zbierać kilkanaście bibliotek i modlić się, żeby ich wersje do siebie pasowały, dopisujesz jeden wiersz.

Tak wygląda fragment pliku pom.xml (pliku, w którym narzędzie Maven opisuje zależności projektu) z naszej księgarni. Trzy startery dają komplet: aplikację WWW, szablony HTML i podgląd stanu aplikacji. Zwróć uwagę, że przy zależnościach nie ma numerów wersji - dobiera je za nas nadrzędny projekt startowy Spring Boot:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<dependencies>
<!-- Web + REST + wbudowany serwer Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Szablony HTML renderowane na serwerze -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Podgląd stanu aplikacji: health, metryki -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Spring Initializr: generujemy szkielet projektu
Skąd wziąć gotowy projekt z odpowiednim pom.xml? Nie trzeba go pisać ręcznie. Służy do tego Spring Initializr (start.spring.io) - internetowy generator startowych projektów Spring Boot. Wybierasz narzędzie do budowania (my użyliśmy Mavena), język, wersję Javy, wpisujesz nazwę projektu i zaznaczasz zależności. Generator pakuje gotowy projekt do pobrania.

Po lewej stronie ustawiasz typ projektu i dane, po prawej dokładasz zależności (to właśnie startery). Przycisk Generate pobiera archiwum z kompletnym, gotowym do uruchomienia szkieletem. To standardowy pierwszy krok każdego projektu w Spring Boot.
Pierwsza aplikacja: budujemy i uruchamiamy
W wygenerowanym projekcie jest jedna klasa startowa. To od niej wszystko się zaczyna. Adnotacja @SpringBootApplication mówi Springowi: to jest aplikacja Spring Boot, włącz swoje mechanizmy i poszukaj moich komponentów. Metoda main uruchamia cały framework.
@SpringBootApplication
public class BookstoreApplication {
public static void main(String[] args) {
SpringApplication.run(BookstoreApplication.class, args);
}
}
Projekt budujemy Mavenem. Polecenie mvn clean package kompiluje kod i pakuje wszystko do jednego pliku JAR - razem z bibliotekami i wbudowanym serwerem w środku. To właśnie dzięki temu aplikację da się później uruchomić jednym poleceniem, bez instalowania osobnego serwera.

Teraz uruchamiamy aplikację poleceniem java -jar. I tu pojawia się znak rozpoznawczy Spring Boot - charakterystyczny napis Spring w konsoli, a pod nim log startu. Widać w nim, że wbudowany serwer Tomcat wstał na porcie 8085, że nasz komponent zgłosił sześć książek w katalogu, i że cała aplikacja wstała w niecałe sześć sekund:

Auto-konfiguracja: magia, która magią nie jest
Skąd Spring Boot wiedział, że ma uruchomić serwer Tomcat na porcie i przygotować obsługę żądań WWW, skoro nigdzie tego nie konfigurowaliśmy? To zasługa auto-konfiguracji. Działa ona według prostej zasady: Boot patrzy, jakie biblioteki masz na ścieżce klas (to znaczy wśród zależności projektu) i na tej podstawie sam włącza rozsądne, domyślne ustawienia.

To nie jest magia - to zwykłe sprawdzanie warunków. Spring Boot potrafi pokazać, co i dlaczego skonfigurował. Wystarczy uruchomić aplikację z opcją --debug, a w konsoli pojawi się raport oceny warunków. Widać w nim, że obsługa żądań i serwer Tomcat zostały włączone, bo odpowiednie klasy są na ścieżce klas (sekcja Positive matches). I odwrotnie: konfiguracje pod inne serwery, jak Jetty, Netty czy Undertow, zostały pominięte, bo tych bibliotek u nas nie ma (sekcja Negative matches):

REST w praktyce: dane jako JSON
Aplikacja działa, więc sprawdźmy, co potrafi. Pokazany wcześniej kontroler był oznaczony adnotacją @RestController. To znaczy, że zwraca dane, a nie stronę HTML. Adnotacja @GetMapping podpina metodę pod adres, a Spring sam zamienia zwróconą listę obiektów na JSON - lekki, tekstowy format wymiany danych, który rozumie niemal każdy język programowania i każda przeglądarka.
Odpytajmy nasz adres /api/books narzędziem curl. Za pierwszym razem prosimy o cały katalog, a za drugim dokładamy parametr ?category=Spring. Ten parametr trafia do metody przez adnotację @RequestParam i zawęża wynik tylko do książek z kategorii Spring - bez pisania ani linijki dodatkowego kodu obsługi adresu:

Jak Spring obsługuje żądanie (Spring MVC)
Skąd Spring wie, że żądanie spod adresu /api/books ma trafić akurat do naszej metody? Odpowiada za to Spring MVC - moduł do obsługi żądań sieciowych. Sercem jest w nim jeden komponent o nazwie DispatcherServlet, czyli centralny dyspozytor. Każde żądanie przychodzące do aplikacji przechodzi najpierw przez niego, a on kieruje je do właściwej metody kontrolera. Prześledźmy tę drogę:

Ten sam DispatcherServlet, który widzieliśmy w logu startu, obsługuje wszystkie żądania - i te zwracające dane (JSON), i te zwracające strony HTML. Różnica jest tylko w tym, co kontroler postanowi oddać.
Strona HTML po stronie serwera (Thymeleaf)
Katalog książek, który widziałeś na samej górze artykułu, to nie JSON, tylko gotowa strona HTML. Zbudował ją Thymeleaf - silnik szablonów, który po stronie serwera wstawia dane do szablonu HTML i oddaje przeglądarce gotową stronę. Tym razem kontroler jest oznaczony zwykłym @Controller (a nie @RestController) i zwraca nazwę szablonu:
@Controller
public class ViewController {
private final BookstoreService service;
public ViewController(BookstoreService service) {
this.service = service;
}
@GetMapping("/")
public String catalog(Model model) {
model.addAttribute("books", service.findAll());
return "catalog"; // nazwa szablonu: templates/catalog.html
}
}
W samym szablonie używamy atrybutów Thymeleaf. Atrybut th:each przechodzi w pętli po liście książek i powiela kartę, a th:text wstawia w wyznaczone miejsca konkretne wartości. To dlatego jedna karta w szablonie zamienia się w sześć kart na gotowej stronie:
<article class="card" th:each="b : ${books}">
<span class="badge" th:text="${b.category}">Kategoria</span>
<h2 th:text="${b.title}">Tytuł książki</h2>
<p class="author" th:text="${b.author}">Autor</p>
<span th:text="${b.price} + ' zł'">cena</span>
</article>
Actuator: gotowość produkcyjna
Ostatni element, który dołożyliśmy, to Actuator. To moduł Spring Boot, który wystawia gotowe adresy do podglądu i monitorowania stanu działającej aplikacji: jej kondycji, metryk czy listy komponentów. W praktyce to on odpowiada za produkcyjną dojrzałość aplikacji Spring Boot. Włączamy go i wybieramy, co ma pokazywać, w pliku application.properties:
spring.application.name=bookstore
server.port=8085
# Actuator - wystawiamy punkty monitorujące stan aplikacji
management.endpoints.web.exposure.include=health,info,metrics,beans
management.endpoint.health.show-details=always
Najczęściej odpytywany adres to /actuator/health. Zwraca on kondycję aplikacji i jej składowych. W naszym przypadku wszystko jest na zielono - status UP, a pod nim stan miejsca na dysku, testu ping i certyfikatów:

Ekosystem Spring to nie jeden projekt
Spring to dziś cała rodzina projektów zbudowanych na wspólnym fundamencie. Poza samym rdzeniem i Spring Boot warto znać kilka nazw, które prędzej czy później spotkasz:

Szczególnie warto zapamiętać Spring Cloud. To zestaw narzędzi do budowy aplikacji rozproszonych, czyli takich, które zamiast jednego dużego programu składają się z wielu małych współpracujących usług (mikroserwisów). To właśnie Spring Boot najczęściej stoi za pojedynczym mikroserwisem. Jeśli chcesz zrozumieć, kiedy w ogóle warto dzielić aplikację na mniejsze części, zajrzyj do naszego osobnego przewodnika monolit kontra mikroserwisy - dobrze uzupełnia to, co pokazaliśmy tutaj.
Podsumowanie
Spring Framework to modularny szkielet do budowy aplikacji w Javie, którego sercem jest wstrzykiwanie zależności: to kontener tworzy obiekty i sam podaje je tam, gdzie są potrzebne. Spring Boot to nadbudowa nad tym szkieletem, która dzięki starterom i auto-konfiguracji sama dobiera ustawienia, ma wbudowany serwer i pozwala uruchomić aplikację jednym poleceniem. W naszej małej księgarni zobaczyliśmy oba w akcji: od wygenerowania projektu, przez budowę i start z charakterystycznym banerem, po REST zwracający JSON, stronę HTML z Thymeleaf i podgląd kondycji przez Actuator.
Najlepszy sposób nauki to praktyka: wygeneruj własny projekt na Spring Initializr, dodaj kontroler z jedną metodą i uruchom aplikację, dokładnie tak jak pokazaliśmy. A jeśli chcesz przejść przez to z trenerem praktykiem i poukładać wiedzę o Springu w dobrej kolejności - od wstrzykiwania zależności, przez Spring MVC, po dostęp do danych i transakcje - mamy dla Ciebie szkolenie.
Chcesz opanować Springa od praktyka?
Szkolenie Spring Framework - szkolenie kompleksowe przeprowadzi Cię przez cały framework na żywym kodzie: wstrzykiwanie zależności, konfigurację, Spring MVC, dostęp do danych i transakcje. Szkolenie ma termin gwarantowany, czyli odbędzie się na pewno.
Komentarze (0)
Brak komentarzy...