Skip to content

Latest commit

 

History

65 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Spring Boot Playground Modules

Spring Boot Java License Documentation

This project is a multi-module Spring Boot playground showcasing REST APIs, configuration, security, OpenAPI, JPA + Flyway, Thymeleaf MVC, Admin monitoring, and AI integrations.

1. Module Map

  • spring-boot-rest-sample — REST API basics

  • spring-boot-config-sample — Externalized configuration patterns

  • spring-boot-admin-server — Admin UI for monitoring apps

  • spring-boot-config-server — Centralized configuration service

  • spring-boot-data-jpa-flyway — Persistence + migrations

  • spring-boot-openapi-root — Parent for OpenAPI samples

  • spring-boot-thymeleaf — Server-side MVC with Thymeleaf

  • spring-boot-security-root — Parent for security samples

  • spring-boot-security-basic — Basic auth demo

  • spring-boot-security-jwt — JWT-based security demo

  • spring-boot-ai-samples — Spring AI integrations

  • spring-boot-batch-parent — Parent for Spring Batch samples

  • spring-boot-batch-restart — JobInstance identity, restart vs a new run

  • spring-boot-batch-flow — Conditional flow, ExitStatus routing, deciders

  • spring-boot-batch-tasklet — Tasklet vs chunk, CONTINUABLE paging

  • spring-boot-batch-partition — Partitioned steps, stride vs contiguous

  • spring-boot-batch-jobstep — A Job nested in a step, and the extractor that decides its identity

  • spring-boot-batch-split — Parallel flows, and a reusable Flow shared by two jobs

  • spring-boot-batch-promotion — Passing a value between steps, and the three ways it does not arrive

  • openapi-sample (under spring-boot-openapi-root) — Concrete OpenAPI demo

2. spring-boot-rest-sample

A reference REST service demonstrating: - CRUD endpoints with JSON payloads - Bean validation and error handling - Controller advice for exceptions - Meaningful HTTP status codes

How to run: - mvn spring-boot:run - Explore endpoints under /api

Learning goals: - Build clean REST controllers - Structure DTOs/validation - Centralize error responses

3. spring-boot-config-sample

Externalized configuration patterns: - application.yaml + profiles - @ConfigurationProperties with validation - Metadata via configuration processor

How to run: - Use -Dspring.profiles.active=dev|prod to switch environments

Learning goals: - Safe, typed configuration binding - Profile-specific settings - Documenting configuration

4. spring-boot-admin-server

Admin console to monitor Spring Boot apps: - Health, metrics, environment, logs - JVM info and endpoints overview

How to run: - Start this server, then register client apps with admin client starter

Learning goals: - Observability with minimal setup - Operational visibility for teams

5. spring-boot-config-server

Centralized configuration with Spring Cloud Config: - Git-backed properties - Environment-specific files - Encryption/decryption support

How to run: - Point spring.cloud.config.server.git.uri to a repo - Clients fetch configuration at bootstrap

Learning goals: - Central config management - Safe secret handling and refresh

6. spring-boot-data-jpa-flyway

Persistence + migrations: - Spring Data JPA repositories - Transactions and entity mapping - Flyway versioned migrations

How to run: - mvn spring-boot:run (Flyway runs at startup)

Learning goals: - Reliable schema evolution - Clean repository design - Testing data layers

7. spring-boot-openapi-root

Parent for OpenAPI samples providing: - Dependency and plugin alignment - Shared conventions for documentation

Learning goals: - Organizing API documentation across modules

7.1. openapi-sample

Concrete OpenAPI demo: - springdoc-openapi integration - Swagger UI - Schemas and validation annotations

How to run: - mvn spring-boot:run - Visit /swagger-ui.html

Learning goals: - API-first documentation - Accurate schemas and examples

8. spring-boot-thymeleaf

Server-side MVC with Thymeleaf: - Templates, fragments, and layouts - Form binding and validation - I18n support

How to run: - mvn spring-boot:run - Visit web UI routes

Learning goals: - Productive SSR development - Clean template composition

9. spring-boot-security-root

Parent for security samples: - Baseline dependencies - Shared configuration practices

Learning goals: - Consistent security setup across modules

9.1. spring-boot-security-basic

Basic authentication: - In-memory users/roles - Form login and protected routes

How to run: - mvn spring-boot:run - Access a protected endpoint to see login flow

Learning goals: - Security filter chain basics - Method and URL-based authorization

9.2. spring-boot-security-jwt

JWT-based security: - Token issuance/validation - Stateless sessions - Role-based access

How to run: - Obtain token via auth endpoint - Call protected APIs with Authorization: Bearer <token>

Learning goals: - API security at scale - Token lifecycle and best practices

10. spring-boot-ai-samples

AI integrations with Spring AI: - Chat/completions flows - Prompt engineering patterns - Retry and observability

How to run: - Configure AI provider credentials via env/properties - mvn spring-boot:run

Learning goals: - Safely integrate AI services - Reusable abstractions for AI features

11. spring-boot-batch-parent

Seven Spring Batch samples, one mechanism each. They assert behaviour rather than describe it: every claim below is a passing test, and several of them replaced an assumption that turned out to be wrong when measured.

11.1. spring-boot-batch-restart — what "continue" actually means

A job continues only when an interrupted execution is relaunched with the same identifying parameters. New content is a new JobInstance, not a re-run.

  • identical parameters after completion are refused with JobInstanceAlreadyCompleteException

  • different parameters make a new instance and everything runs again

  • an interrupted instance relaunched resumes — the already-completed step’s body does not execute a second time

  • a random per-launch token orphans the failed instance and silently redoes the work

The finding nobody asks for: Boot 4 ships no JDBC job-repository auto-configuration. Add spring-boot-starter-batch and a DataSource and you get ResourcelessJobRepository. The job runs, every step executes, it reports COMPLETED — and nothing is written down. Every execution is id 1, no BATCH_* table is created, and restart therefore does not exist. Not refused: absent. Nothing warns you, so ResourcelessDefaultTest pins it.

11.2. spring-boot-batch-flow — routing on ExitStatus

BatchStatus is the recorded outcome; ExitStatus is a string and the only thing on(…​) matches. A step that succeeded can still route away from the happy path by returning a custom ExitStatus from a StepExecutionListener.

A JobExecutionDecider answers a question about the state of the world without doing work — and leaves no StepExecution behind, so "the gate was shut" stays distinguishable from "the gate was open and there was nothing to do".

11.3. spring-boot-batch-tasklet — the two step shapes

A chunk step commits every N items with the framework owning the loop; a tasklet is one method you own. Returning RepeatStatus.CONTINUABLE re-invokes it in a new transaction each time, which is how a long sweep commits as it goes and survives being killed halfway.

Two assumptions here were wrong before measurement: Batch 6 does not add a final empty commit (7 items at chunk 3 is 3 commits, not 4), and the current transaction’s name cannot distinguish two transactions — it is derived from the step and is identical every time. Counting afterCommit callbacks can.

11.4. spring-boot-batch-partition — stride, not contiguous

Worker k taking [k*size, (k+1)*size) is correct and usually the wrong choice: ids follow insertion order, which follows walk order, so one contiguous block lands on the expensive material and the step takes as long as its unluckiest worker.

Measured at 60 items over 4 workers with cost clustered at one end:

scheme slowest worker vs a perfect split

stride

18,895

1.08x

contiguous

40,840

2.33x

Both cover the same population; only the balance differs. The slowest worker is the wall clock, which is the number the test asserts on.

11.5. spring-boot-batch-jobstep — a whole Job inside a step

A JobStep runs jobOperator.start(childJob, parameters). The child gets its own JobExecution and its own JobInstance; the parent sees one step and an exit status, not the child’s steps. A nested failure surfaces as UnexpectedJobExecutionException — the parent is told that the child failed, never why.

Nothing else crosses the boundary: a value the child promotes into the child’s job execution context is invisible to the parent, whose job execution context is a different object that nothing copies into. One ExitStatus string per nesting level is the whole channel.

The child’s restart identity is whatever the JobParametersExtractor hands it, and that one object decides everything:

  • no extractor — the default copies every parent parameter, identifying flags intact, so the child instance tracks the parent’s. Relaunching a failed parent resumes the child: the completed parent step and the completed child step both stay done. Restart nests, and you get it by writing nothing.

  • a fixed extractor — "the child always imports the catalogue" is the obvious thing to write and it makes the parent succeed exactly once. The second parent run, a legitimately new instance, dies on JobInstanceAlreadyCompleteException thrown from inside the JobStep.

The finding that changes how you read the API: JobStep caches the extracted parameters in its own step execution context on first use and reads them back on restart. An extractor that stamps each launch with a fresh value is called once, not twice — and that cache is not an optimisation, it is the only reason a nested restart can land on the same child instance at all.

11.6. spring-boot-batch-split — parallel flows, and a Flow worth extracting

split(TaskExecutor) runs flows concurrently and joins. Two things decide how to read any split:

  • the flow passed to start(…​) is itself a branch — start(a).split(e).add(b) runs a and b at the same time. The branch count is one more than the number of add(…​) arguments. Pinned by a rendezvous latch: neither branch can leave until the other has arrived, which sequential execution cannot satisfy.

  • a failing branch does not cancel its siblings — every branch runs to completion, then the worst status wins. A split costs its slowest branch even when another failed in the first second.

A Flow bean built with FlowBuilder is shared by two jobs here, and each job execution gets its own step executions. A Flow is structure, not an instance: naming the same flow twice inside one job adds no second traversal.

The one that cost the most to find: a branch that completes and reports a custom ExitStatus — the ordinary routing signal the flow sample is built on — ends the job FAILED, with no failure exception and an empty exit description. Every step in it completed. Running that branch alone fails identically, so it is not a property of split at all: a flow whose step ends on a status no transition matches ends FAILED. A split only makes it harder to see, because the job status is then the only evidence and it does not say which branch produced it.

11.7. spring-boot-batch-promotion — the value that did not arrive

A step’s ExecutionContext is invisible downstream; the job’s is visible to every later step. ExecutionContextPromotionListener is the bridge, and it is opt-in per key, per exit status, and silent:

  • a key you did not name reads back null in the later step — not an error, not a warning, and indistinguishable from a value that was never computed. It is still there, in the writing step’s own context, where nothing downstream will look.

  • promotion defaults to statuses = {"COMPLETED"}. A step that completes and reports a custom exit status promotes nothing, including the key you did name — so the step that succeeded and routed, the one whose numbers you most want, is the case the default excludes.

The surprise: strict = true does not make it loud. The listener does raise IllegalArgumentException, and AbstractStep catches every exception a listener throws from afterStep and logs it. The step completes, the job completes, nothing is recorded against the step execution, the next step runs with nothing to read, and the only trace is one ERROR line. A misspelled promotion key is not detectable from a job’s outcome under either setting — if it matters, the reading step has to assert on what it read.

About

A hands-on learning hub for Spring Boot, showcasing REST APIs, security, JPA, Thymeleaf, OpenAPI, admin monitoring, and AI integrations. Perfect for developers mastering modern Spring Boot applications.

Topics

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages