Java/03core13 min

Spring Boot — podstawy

Spring Boot to standard budowy serwisów Java w ProfessNet. Daje gotowy kontener DI, serwer aplikacyjny i autokonfigurację. Ta lekcja pokazuje podstawowe wzorce: warstwy, kontrolery, wstrzykiwanie i konfigurację.

Warstwy

Serwis Spring Boot dzielimy na warstwy o jasnych odpowiedzialnościach:

controller  →  service  →  repository  →  baza danych
   (HTTP)      (logika)    (dostęp do danych)

Standard ProfessNet: kontroler nie zawiera logiki domenowej — waliduje wejście, woła service i zwraca odpowiedź. Logika żyje w @Service, dostęp do danych w @Repository.

Kontroler REST

@RestController
@RequestMapping("/api/v1/inventory")
public class InventoryController {

  private final InventoryService service;

  public InventoryController(InventoryService service) {
    this.service = service;
  }

  @PostMapping("/scan")
  @ResponseStatus(HttpStatus.ACCEPTED)
  public ScanResult scan(@Valid @RequestBody ScanRequest request) {
    return service.runScan(request.forest());
  }
}

Wstrzykiwanie przez konstruktor

Zależności wstrzykujemy przez konstruktor, a pola robimy final. Unikamy @Autowired na polach — utrudnia testowanie i ukrywa zależności.

// dobrze — constructor injection, pola final
@Service
public class InventoryService {
  private final ProbeRepository repository;

  public InventoryService(ProbeRepository repository) {
    this.repository = repository;
  }
}

// źle — field injection
@Service
public class BadService {
  @Autowired private ProbeRepository repository;   // trudne do testów
}

Standard ProfessNet: constructor injection zawsze. Spring od wersji 4.3 wstrzykuje automatycznie, gdy klasa ma jeden konstruktor — @Autowired nie jest potrzebne.

Walidacja wejścia

DTO walidujemy adnotacjami Bean Validation. @Valid w kontrolerze uruchamia walidację, a błędy stają się czytelnym 400.

public record ScanRequest(
    @NotBlank @Size(max = 255) String forest,
    boolean deep) {}

DTO jako record

Do przenoszenia danych używamy record — niezmienne, zwięzłe, bez boilerplate.

public record ScanResult(String jobId, String forest, Instant enqueuedAt) {}

Konfiguracja

Ustawienia trzymamy w application.yml, a sekrety wstrzykujemy ze zmiennych środowiskowych — nigdy nie zaszywamy ich w kodzie.

spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
zeus:
  scan:
    max-concurrency: 10

Typowane właściwości wstrzykujemy przez @ConfigurationProperties:

@ConfigurationProperties(prefix = "zeus.scan")
public record ScanProperties(int maxConcurrency) {}
AdnotacjaRola
@RestControllerwarstwa HTTP, zwraca JSON
@Servicelogika domenowa
@Repositorydostęp do danych
@Valid + Bean Validationwalidacja DTO
@ConfigurationPropertiestypowana konfiguracja

Wskazówka: sekrety (DB_PASSWORD) zawsze przez ${ENV}. W repo trzymamy application.yml bez wartości sekretnych — te dostarcza środowisko.


Czysty podział na warstwy, constructor injection, walidacja DTO i konfiguracja poza kodem — to fundament każdego serwisu Spring Boot w ProfessNet. Na tej bazie budujemy obsługę wyjątków, logowanie i testy.