Wzorce Java i Spring Boot
ENReceptury Java i Spring Boot dla agentów AI sprowadzają się do trzech ruchów: przypięcia Spring Boot 4.1, Hibernate 7 i Spring Security 7 w kontekście agenta, żeby przestał pisać usunięte API, proszenia agenta o określone zachowanie w razie błędu zamiast o „CRUD” oraz oddania dowodu testom Testcontainers, testowi liczby zapytań i regułom ArchUnit zamiast czytania linijka po linijce.
Ta receptura jest dla programistów Javy, którzy oddają Claude Code, Codexowi lub Cursorowi pisanie serwisów Spring Boot. Kontroler od agenta się kompiluje, a testy na mockach przechodzą. Potem build pada na @Where, którego Hibernate 7 już nie ma, lista zamówień wykonuje jedno zapytanie na wiersz, a timeout płatności kończy się błędem 500, bo nikt nie podpiął circuit breakera.
Co dają te receptury Spring Boot
Dział zatytułowany „Co dają te receptury Spring Boot”- Blok przypinający wersje, który powstrzymuje agenta przed pisaniem kodu pod Spring Boot 3, Hibernate 6 i Security 6
- Gotowe prompty na zasób REST, encje JPA z soft delete, odporny klient usługi zewnętrznej, zabezpieczenie JWT przez resource server i zestaw testów Testcontainers
- Test liczby zapytań, który przerywa build przy regresji N+1
- Reguły ArchUnit odrzucające wstrzykiwanie przez pola, dostęp kontrolerów do repozytoriów i wyciek encji, bez czytania diffa przez człowieka
- Listę typowych awarii z procedurą wyjścia z każdej
Najpierw przypnij wersje Springa w kontekście agenta
Dział zatytułowany „Najpierw przypnij wersje Springa w kontekście agenta”Modele uczyły się na latach kodu Spring Boot 2 i 3, więc prompt bez przypiętych wersji produkuje API, których już nie ma. Aktualne wersje według BOM-u spring-boot-dependencies 4.1.1 z Maven Central (wydanie 2026-08-20, sprawdzone 2026-09-26): Spring Framework 7.0.9, Spring Security 7.1.1, Spring Kafka 4.1.1, Hibernate ORM 7.4.5, Jackson 3.1.5 i Testcontainers 2.0.5. Spring Boot 4.1 wymaga Javy 17 lub nowszej.
Wstaw ten blok do pliku, który agent czyta przy każdym uruchomieniu:
## Stack (do not downgrade, do not use older APIs)- Spring Boot 4.1.x, Java 25, Maven wrapper (./mvnw)- Spring Framework 7, Spring Security 7 (lambda DSL only), Hibernate ORM 7, Jackson 3, Testcontainers 2- Starters: spring-boot-starter-webmvc (not -web, deprecated), spring-boot-starter-security-oauth2-resource-server- Banned: @Where (removed in Hibernate 7; use @SoftDelete or @SQLRestriction), WebSecurityConfigurerAdapter, .and() and authorizeRequests() in security config, ListenableFuture/addCallback on KafkaTemplate, @MockBean (use @MockitoBean), org.testcontainers.containers.PostgreSQLContainer (use org.testcontainers.postgresql)## Conventions- Constructor injection only; no @Autowired fields- Controllers return DTO records, never entities; no business logic or try/catch in controllers- One @RestControllerAdvice returning ProblemDetail (application/problem+json)- @Transactional on public service methods only; readOnly = true for reads- Every endpoint ships with a Testcontainers integration testZapisz blok w CLAUDE.md w katalogu głównym repozytorium. Claude Code wczytuje go na początku każdej sesji. Resztę zawartości tego pliku opisuje inicjalizacja projektu.
Zapisz blok w AGENTS.md w katalogu głównym repozytorium. Codex czyta go przy każdym zadaniu. Zobacz AGENTS.md dla Codexa.
Zapisz blok jako regułę projektu stosowaną zawsze. Format pliku opisuje strona reguły projektu w Cursorze.
Szkielet serwisu Spring Boot 4.1 wygenerowany przez agenta
Dział zatytułowany „Szkielet serwisu Spring Boot 4.1 wygenerowany przez agenta”Źródłem prawdy dla drzewa zależności jest Spring Initializr pod adresem start.spring.io. Niech agent pobierze projekt stamtąd, zamiast pisać pom.xml z pamięci, bo właśnie w ręcznie pisanym POM-ie wracają stare nazwy starterów i nieaktualne wersje. Prompt jest ten sam w każdym narzędziu; różni się tylko miejsce uruchomienia.
claude "Download a Spring Boot 4.1 Maven project for Java 25 from start.spring.io with web, data-jpa, validation, security, oauth2-resource-server, actuator, postgresql, flyway and testcontainers. Then check pom.xml against the Stack section of CLAUDE.md and fix any starter or version that differs. Run ./mvnw -q verify and show me the result."codex --worktree "Download a Spring Boot 4.1 Maven project for Java 25 from start.spring.io with web, data-jpa, validation, security, oauth2-resource-server, actuator, postgresql, flyway and testcontainers. Then check pom.xml against the Stack section of AGENTS.md and fix any starter or version that differs. Run ./mvnw -q verify and show me the result."--worktree uruchamia sesję w nowym, zarządzanym worktree Gita, więc szkielet nie koliduje z twoją bieżącą gałęzią.
Wklej ten sam prompt do panelu Agent, zamieniając AGENTS.md na „the project rules”. Zanim zaakceptujesz proponowany diff pom.xml, przejrzyj go: ten plik decyduje o każdej wersji, na której opiera się reszta strony.
Szkielet jest gotowy, gdy ./mvnw verify przechodzi, a pom.xml zawiera spring-boot-starter-webmvc zamiast przestarzałego spring-boot-starter-web.
Zasób REST, który poprawnie obsługuje błędy
Dział zatytułowany „Zasób REST, który poprawnie obsługuje błędy”Proś o zachowanie w razie błędu, nie o „CRUD”. Bez tego agent zwraca encje, rozrzuca bloki try/catch, które logują i zwracają null, a na błąd walidacji odpowiada stack trace’em.
Poprawny kontroler pozostaje chudy. Jeśli wygenerowany mapuje pola, łapie wyjątki albo wstrzykuje EntityManager, odeślij go z poleceniem „move mapping and error handling out of the controller; rely on the advice”:
@RestController@RequestMapping("/api/v1/products")class ProductController { private final ProductService products;
ProductController(ProductService products) { // constructor injection, no @Autowired this.products = products; }
@GetMapping Page<ProductResponse> list(@PageableDefault(size = 20) Pageable pageable, @RequestParam(required = false) String search) { return products.search(search, pageable); // mapping lives in the service }}Soft delete encji JPA w Hibernate 7
Dział zatytułowany „Soft delete encji JPA w Hibernate 7”Poproś o soft delete, a agenci często generują parę z Hibernate 5: @SQLDelete i @Where. Adnotacji @Where w ogóle nie ma w Hibernate ORM 7.4.5 (sprawdzone w opublikowanym JAR-ze 2026-09-26), więc taki kod się nie skompiluje. Zarządzana adnotacja @SoftDelete zastępuje obie i odfiltrowuje usunięte wiersze w każdym zapytaniu.
@Entity@Table(name = "orders")@SoftDelete // Hibernate 6.4+: replaces @SQLDelete + @Where on every query@EntityListeners(AuditingEntityListener.class)class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false) private Customer customer; // items, status, auditing fields}Głębszą pracę z JPA, w tym pułapki leniwego ładowania w różnych ORM-ach, opisuje strona dobre praktyki ORM. Migracje Flyway opisują wzorce migracji baz danych.
Wyłap zapytania N+1 testem liczby zapytań
Dział zatytułowany „Wyłap zapytania N+1 testem liczby zapytań”Zapytania N+1 to częsta wada wydajnościowa w generowanym JPA: asocjacja LAZY czytana w pętli wykonuje jedno zapytanie na wiersz i przy pięciu wierszach danych testowych wygląda niewinnie. Test, który liczy instrukcje SQL, zamienia ją w czerwony build. Włącz statystyki w profilu testowym przez spring.jpa.properties.hibernate.generate_statistics=true, a potem sprawdź licznik:
@Testvoid listingOrdersDoesNotScaleQueriesWithRows() { fixtures.createOrders(50); // enough rows for an N+1 to show var stats = entityManagerFactory.unwrap(SessionFactory.class).getStatistics(); stats.clear();
mockMvc.perform(get("/api/v1/orders?size=50")).andExpect(status().isOk());
assertThat(stats.getPrepareStatementCount()).isLessThanOrEqualTo(3);}Gdy test zrobi się czerwony, wklej błąd agentowi: „the list endpoint runs 51 statements; fix it with an @EntityGraph on to-one associations and batch fetching for collections, not by raising the limit”. Nie akceptuj JOIN FETCH kolekcji w zapytaniu stronicowanym: Hibernate stronicuje wtedy w pamięci (zobacz typowe awarie niżej).
Odporne wywołania usług zewnętrznych z Resilience4j
Dział zatytułowany „Odporne wywołania usług zewnętrznych z Resilience4j”Serwis, który synchronicznie woła płatności albo magazyn, potrzebuje timeoutów i circuit breakera. Spring Framework 7.0 zawiera @Retryable i @ConcurrencyLimit w pakiecie org.springframework.resilience.annotation (włączane przez @EnableResilientMethods), ale nie ma circuit breakera, więc do tego użyj resilience4j-spring-boot4 2.4.0. W Kafce KafkaTemplate.send() zwraca CompletableFuture; stare ListenableFuture.addCallback(...) się nie kompiluje.
Publisher powinien wyglądać tak: whenComplete i dopiero po commicie, żeby wycofane zamówienie nigdy nie wygenerowało zdarzenia:
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)void onOrderCreated(OrderCreatedEvent event) { var message = OrderEvent.from(event); kafkaTemplate.send("order-events", message.orderId(), message).whenComplete((result, ex) -> { if (ex != null) log.error("Failed to publish {}", message.eventId(), ex); });}Ponowienia, klucze idempotencji i kompensację między serwisami opisują wzorce odzyskiwania.
Zabezpiecz API przez Spring Security 7 i JWT
Dział zatytułowany „Zabezpiecz API przez Spring Security 7 i JWT”Spring Security 7.1.1 nie ma WebSecurityConfigurerAdapter, and() ani authorizeRequests(): każda metoda HttpSecurity przyjmuje lambdę Customizer (sprawdzone w opublikowanym JAR-ze spring-security-config 2026-09-26). Agenci lubią też ręcznie pisać OncePerRequestFilter, który parsuje JWT. Nie akceptuj tego. OAuth2 resource server sam sprawdza podpis, ważność, wystawcę i odbiorcę tokenu.
Testy bezpieczeństwa wykraczające poza łańcuch filtrów opisuje strona testy bezpieczeństwa z AI.
Udowodnij to na prawdziwym Postgresie z Testcontainers 2
Dział zatytułowany „Udowodnij to na prawdziwym Postgresie z Testcontainers 2”Zamockowane repozytoria przechodzą, choć prawdziwe zapytanie jest zepsute, dlatego konwencje wymagają prawdziwej bazy. Testcontainers 2 przeniósł moduł Postgresa do artefaktu testcontainers-postgresql i pakietu org.testcontainers.postgresql; stara klasa org.testcontainers.containers.PostgreSQLContainer jest oznaczona jako przestarzała. Dzięki @ServiceConnection Spring Boot sam podpina datasource pod kontener, więc test nie potrzebuje @DynamicPropertySource.
@TestConfiguration(proxyBeanMethods = false)class PostgresTestConfig { @Bean @ServiceConnection PostgreSQLContainer postgres() { // org.testcontainers.postgresql, not .containers return new PostgreSQLContainer("postgres:17"); }}Wzorce Testcontainers wykraczające poza warstwę bazy opisuje strona testy integracyjne z AI.
Daj agentowi żywy schemat przez serwer MCP dla Postgresa
Dział zatytułowany „Daj agentowi żywy schemat przez serwer MCP dla Postgresa”Przy pracy nad indeksami i zapytaniami agent radzi sobie lepiej, gdy czyta prawdziwe indeksy i wynik EXPLAIN, niż gdy zgaduje na podstawie klas encji. Daje to Postgres MCP Pro (PyPI postgres-mcp 0.3.0, od Crystal DBA). Ostatnie wydanie na PyPI pochodzi z maja 2025, więc zanim zrobisz z niego standard zespołu, sprawdź aktywność projektu; alternatywy, DBHub i MCP Toolbox, opisuje strona serwery MCP dla baz danych. Skieruj go na bazę deweloperską z rolą tylko do odczytu:
claude mcp add -s project postgres -e 'DATABASE_URI=${DEV_DATABASE_URL}' \ -- uvx postgres-mcp --access-mode=restrictedPojedyncze cudzysłowy zostawiają placeholder dosłownie, więc .mcp.json przechowuje ${DEV_DATABASE_URL}, a Claude Code rozwija go ze zmiennych środowiskowych przy starcie; dane logowania nigdy nie trafiają do repozytorium.
Codex (codex mcp add … --env …) i Cursor przyjmują to samo polecenie i argumenty we własnej konfiguracji MCP. Strona serwery MCP dla baz danych zawiera wszystkie trzy konfiguracje i rolę tylko do odczytu, którą trzeba najpierw utworzyć.
Jak zweryfikować kod Springa od agenta bez czytania każdej linijki
Dział zatytułowany „Jak zweryfikować kod Springa od agenta bez czytania każdej linijki”Przeczytaj SQL migracji, reguły bezpieczeństwa i nazwy testów. Resztę niech sprawdzą bramki. ArchUnit 1.5.1 zamienia konwencje w testy, więc zignorowana przez agenta konwencja przerywa build, zamiast czekać, aż zauważy ją recenzent:
@AnalyzeClasses(packages = "com.example.shop")class ArchitectureTest { @ArchTest static final ArchRule noFieldInjection = GeneralCodingRules.NO_CLASSES_SHOULD_USE_FIELD_INJECTION;
@ArchTest static final ArchRule controllersNeverTouchRepositories = noClasses() .that().areAnnotatedWith(RestController.class) .should().dependOnClassesThat() // Spring Data's interface, not the @Repository annotation: with the annotation, // areAssignableTo never matches and the rule passes vacuously .areAssignableTo(org.springframework.data.repository.Repository.class);
@ArchTest static final ArchRule controllersNeverReturnEntities = noMethods() .that().areDeclaredInClassesThat().areAnnotatedWith(RestController.class) .should().haveRawReturnType(annotatedWith(Entity.class));}Reguła dla encji sprawdza tylko surowy typ zwracany. Kontroler zwracający Page<Order> albo List<Order> ją przechodzi, więc typy generyczne pokryj własnym ArchCondition, który sprawdza argumenty typu.
Sprawdzenie zakazanych API z bloku wersji dodaj do CI jako jeden krok. Kończy się kodem różnym od zera, gdy znajdzie choć jedno trafienie:
! git grep -nE '@Where\b|WebSecurityConfigurerAdapter|authorizeRequests\(|\.addCallback\(|@MockBean\b|org\.testcontainers\.containers\.PostgreSQLContainer' -- '*.java'| Bramka | Czego dowodzi | Gdzie działa |
|---|---|---|
./mvnw verify z Testcontainers | Endpointy, walidacja, ścieżki 409/404 i soft delete działają na prawdziwym Postgresie | Przed pushem i w CI |
| Testy liczby zapytań | Brak regresji N+1 na endpointach list | CI |
| Reguły ArchUnit | Brak wstrzykiwania przez pola, dostępu kontrolerów do repozytoriów i encji zwracanych z kontrolera | CI |
Powyższy git grep po zakazanych API | Żadne usunięte API nie wróciło | CI |
Testy wycinkowe bezpieczeństwa (@WebMvcTest) | 401, 403 i mapowanie ról działają zgodnie ze specyfikacją | CI |
| Test circuit breakera | Fallback się uruchamia, a 4xx nigdy nie jest ponawiane | CI |
Lektura SQL Flyway i SecurityFilterChain przez człowieka | Blokady, usunięcia i reguły dostępu są zamierzone | Code review PR |
Programista, który otwiera pull request, odpowiada za nazwy testów i niezmienniki, które one kodują. Recenzent zatwierdza migrację, konfigurację bezpieczeństwa i wyniki bramek, a nie każdą wygenerowaną linijkę. Jak spakować te dowody do review, opisuje pakiet dowodów. Dlaczego to zastępuje czytanie diffa, wyjaśnia strona czytanie dowodów zamiast kodu.
Kiedy kod Spring Boot od agenta się psuje
Dział zatytułowany „Kiedy kod Spring Boot od agenta się psuje”- Kod celuje w nieobsługiwaną wersję główną (Spring Boot 3 / Hibernate 6). Objawy:
cannot find symbol: class Where, brakWebSecurityConfigurerAdapter, brakaddCallbacknaCompletableFuturealbo niedziałające importycom.fasterxml.jackson.databindpod Jacksonem 3. Wyjście: dodaj blok wersji doCLAUDE.mdlubAGENTS.md, wklej agentowi błąd kompilatora z dopiskiem „this is Spring Boot 4.1 / Hibernate 7”, a potem dodaj do CI opisany wyżej krokgit greppo zakazanych API. - Testy typu slice (
@WebMvcTest,@DataJpaTest) się nie kompilują. Objaw: po aktualizacji nie da się rozwiązać@WebMvcTestani@DataJpaTest. Wyjście: Spring Boot 4 podzielił autokonfigurację testów na moduły; dodajspring-boot-starter-webmvc-testlubspring-boot-starter-data-jpa-test, zamiast pozwalać agentowi zgadywać nazwy pakietów. @Transactionalnic nie robi. Objaw: częściowy zapis przeżywa wyjątek. Przyczyna: adnotacja jest na metodzieprivatealbo na metodzie wywoływanej z wnętrza tej samej klasy, więc proxy Springa nie widzi wywołania. Wyjście: przenieś ją na publiczną metodę serwisu (i nie stawiaj jej na kontrolerach: transakcje należą do warstwy serwisów), a potem dodaj test, który rzuca wyjątek w połowie zapisu i sprawdza, że wiersz nie istnieje.- Walidacja się nie uruchamia. Objaw: puste nazwy trafiają do bazy. Przyczyna: brak
@Validprzy@RequestBody. Wyjście: test 422 z promptu REST powinien już być czerwony; jeśli jest zielony, test wysyła poprawne dane. - Metoda
@Asyncpołyka wyjątek. Objaw: zadanie po cichu staje. Wyjście: zwracajCompletableFuturealbo zarejestrujAsyncUncaughtExceptionHandleri sprawdź ścieżkę błędu testem. - Zdarzenia dla wycofanych zamówień. Objaw: inne serwisy widzą zamówienia, które nie istnieją. Wyjście: publikuj z
@TransactionalEventListener(phase = AFTER_COMMIT)albo przejdź na transactional outbox. - Hibernate stronicuje listę w pamięci. Objaw: w logu pojawia się ostrzeżenie
HHH90003004(firstResult/maxResults specified with collection fetch; applying in memory), a zużycie pamięci rośnie z rozmiarem tabeli, nie strony. Przyczyna:JOIN FETCHalbo@EntityGraphna kolekcjiitemsw zapytaniu stronicowanym, więc Hibernate wczytuje wszystkie pasujące wiersze i stronicuje w JVM. Wyjście: w zapytaniu stronicowanym pobieraj tylko asocjacje do-jednego, a kolekcje ładuj przezdefault_batch_fetch_sizealbo drugim zapytaniem po identyfikatorach; w testach ustawhibernate.query.fail_on_pagination_over_collection_fetch=true, żeby ostrzeżenie stało się wyjątkiem. - N+1 wraca po refaktoryzacji. Objaw: opóźnienie rośnie z rozmiarem strony. Wyjście: test liczby zapytań powinien być czerwony; jeśli jest zielony, fixture ma za mało wierszy, więc dodaj więcej danych.
Co dalej ze Spring Boot
Dział zatytułowany „Co dalej ze Spring Boot”- Receptury backendowe: przegląd pozostałych języków i frameworków
- Wzorce tworzenia API: projekt REST, wersjonowanie i format błędów niezależnie od języka
- Dobre praktyki ORM: N+1 i leniwe ładowanie w różnych ORM-ach
- Optymalizacja SQL: plany
EXPLAINi projekt indeksów, gdy zapytanie jest wolne, a nie powtarzane - Receptury .NET i C#: te same receptury dla ASP.NET Core
- Wzorce orkiestracji Kubernetes: wdrażanie tych serwisów na klaster
Czy ten artykuł Ci pomógł?
Jedno kliknięcie, bez logowania.
Co z tym zrobisz?
Jak się czytało?
Co przeszkadzało? Możesz zaznaczyć kilka
Twoja rola?
Dzięki. Czytam każdą odpowiedź. Michał Jaskólski
Pomógł Ci ten artykuł?
Każde to jedno kliknięcie. Zamknij, kiedy chcesz.
bez logowania