The core ideas: beans, configuration, REST controllers, validation, database access,
calling other services and testing all of it.
Subsections of Day 1: Spring Boot Basics
Why Spring Boot?
The problem it solves
Plain Java gives you a language, not an application. A service needs an HTTP server, JSON
mapping, a connection pool, transactions, config per environment, health checks, metrics and
tests. Spring Boot wires all of that from a dependency list and sensible defaults.
One dependency and four lines is a running HTTP service.
The three ideas
Idea
What it means
Inversion of control
you declare components, the container creates and connects them
Auto-configuration
classpath contents decide what gets configured
Starters
one dependency per topic, versions pinned by the parent
Every default is replaceable: define your own bean, and the auto-configured one backs off.
The ecosystem
Project
For
Spring Framework 7
core container, MVC/WebFlux, transactions
Spring Data
JPA, MongoDB, Redis repositories
Spring Security
authentication and authorization
Spring Boot Actuator
health, metrics, observability
Spring Batch / Integration / Kafka
jobs, pipelines, messaging
What is new in Spring Boot 4
Modularization — many small jars instead of one spring-boot-autoconfigure
Null safety — the whole portfolio is annotated with JSpecify
API versioning — first-class support in Spring MVC and WebFlux
HTTP service clients — declare an interface, get an implementation
Java 25 support — with Java 17 still the baseline
Resilience — @Retryable and @ConcurrencyLimit moved into the core framework
RestTestClient — one test client for mock and live servers
Built on Spring Framework 7, Jakarta EE 11, Jackson 3 and Kotlin 2.2.
Coming from Spring Boot 3?
Most code compiles unchanged. Watch for: Jackson 2 support deprecated, a few renamed
properties (spring.dao.exceptiontranslation.enabled → spring.persistence.exceptiontranslation.enabled,
management.tracing.enabled → management.tracing.export.enabled), and @MockBean replaced by @MockitoBean.
When not to use it
a 200-line CLI tool — plain Java is lighter
hard real-time or tiny memory budgets — the container costs startup and RAM
a library that others embed — keep it framework-free
★ Exercises
Name three things auto-configuration does for spring-boot-starter-web.
Remove the web starter from a project. What still runs?
Look up two more starters at start.spring.io and say what each adds.
Which of the Boot 4 features above would change code you already have?
Beans & Dependency Injection
What a bean is
A bean is an object the container creates, configures and hands out. You never call new
on it yourself.
All four of the first are @Component underneath — the name documents intent.
Constructor injection
@RestControllerpublicclassGreetingController{privatefinalGreetingServiceservice;publicGreetingController(GreetingServiceservice){// no @Autowired neededthis.service=service;}@GetMapping("/greet/{name}")Stringgreet(@PathVariableStringname){returnservice.greet(name);}}
One constructor, final fields, no framework annotation in sight — that class is testable
with plain new.
Avoid field injection
@Autowired private Foo foo; hides dependencies, breaks final, and forces reflection in
tests. Use the constructor.
Alerts(NotifierdefaultOne,// EmailNotifier, it is @Primary@Qualifier("sms")Notifierurgent){…}
Inject List<Notifier> or Map<String, Notifier> to get all of them.
Conditional beans
@Bean@ConditionalOnMissingBean// only if the user did not define one@ConditionalOnProperty(name="features.cache",havingValue="true")CacheManagercacheManager(){…}
This is exactly how auto-configuration backs off in favour of your own beans.
Scope and lifecycle
Beans are singletons by default — one instance per context, shared by every thread.
Keep them stateless.
@Component@Scope("prototype")// new instance per injection pointclassTicket{}
@ComponentclassWarmup{@PostConstructvoidstart(){…}// after injection@PreDestroyvoidstop(){…}// on shutdown}
A violation throws MethodArgumentNotValidException → 400 before your method runs.
Annotation
Checks
@NotNull / @NotBlank / @NotEmpty
present / non-blank text / non-empty collection
@Size(min, max)
length or size
@Min / @Max / @Positive
numbers
@Email / @Pattern
format
@Past / @Future
dates
Validate parameters too:
@Validated// on the class@GetMapping("/search")List<Book>search(@RequestParam@NotBlankStringq){…}
Your own exceptions
publicclassBookNotFoundExceptionextendsRuntimeException{publicBookNotFoundException(longid){super("No book with id "+id);}}
One place for error responses
@RestControllerAdvicepublicclassApiExceptionHandler{@ExceptionHandler(BookNotFoundException.class)ProblemDetailnotFound(BookNotFoundExceptionex){varpd=ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,ex.getMessage());pd.setTitle("Book not found");returnpd;}@ExceptionHandler(MethodArgumentNotValidException.class)ProblemDetailinvalid(MethodArgumentNotValidExceptionex){varpd=ProblemDetail.forStatus(HttpStatus.BAD_REQUEST);pd.setTitle("Validation failed");pd.setProperty("errors",ex.getBindingResult().getFieldErrors().stream().collect(toMap(FieldError::getField,FieldError::getDefaultMessage)));returnpd;}}
@RestControllerAdvice applies to every controller.
RFC 9457 problem details
ProblemDetail is the standard error format:
{"type":"about:blank","title":"Book not found","status":404,"detail":"No book with id 42","instance":"/api/books/42"}
Turn it on for the framework’s own errors too:
spring.mvc.problemdetails.enabled=true
Shortcut for simple cases
thrownewResponseStatusException(HttpStatus.NOT_FOUND,"No book with id "+id);
Fine in small apps; a typed exception plus advice scales better.
Warning
Never return the stack trace to clients. Log it with the request id and send the client a
short message.
@EntitypublicclassBook{@Id@GeneratedValue(strategy=GenerationType.IDENTITY)privateLongid;@Column(nullable=false)privateStringtitle;privateStringauthor;privateintyear;protectedBook(){}// required by JPApublicBook(Stringtitle,Stringauthor,intyear){…}// getters, and setters only where they make sense}
Warning
Entities are mutable objects tied to a persistence context — not DTOs. Records cannot be
entities. Map to a record before returning data from a controller.
You write no implementation — Spring Data derives the query from the method name.
Inherited for free: save, saveAll, findById, findAll, delete, count,
findAll(Pageable).
Own queries
@Query("select b from Book b where b.year between :from and :to")List<Book>inRange(intfrom,intto);@Query(value="select * from book order by random() limit 1",nativeQuery=true)Bookrandom();
Controllers can take a Pageable parameter directly: ?page=1&size=20&sort=title,asc.
Transactions
@ServicepublicclassBookService{privatefinalBookRepositoryrepo;BookService(BookRepositoryrepo){this.repo=repo;}@TransactionalpublicBookrename(longid,Stringtitle){varbook=repo.findById(id).orElseThrow(()->newBookNotFoundException(id));book.setTitle(title);// dirty checking writes on commitreturnbook;}@Transactional(readOnly=true)publicList<Book>all(){returnrepo.findAll();}}
Rules: @Transactional belongs on the service, rolls back on unchecked exceptions, and only
works when the call comes in from outside the bean (it is a proxy).
Schema management
Setting
Use
spring.jpa.hibernate.ddl-auto=create-drop
tests, demos
…=update
local development, never production
…=validate
production, with Flyway or Liquibase for migrations
Fast, and where the bulk of your tests should live.
Slice tests
Start only the part of the context you need.
Annotation
Loads
@WebMvcTest
controllers, JSON mapping, no services
@DataJpaTest
JPA, repositories, in-memory database
@RestClientTest
HTTP clients with a mock server
@JsonTest
serialization only
@WebMvcTest(BookController.class)classBookControllerTest{@AutowiredRestTestClientclient;@MockitoBeanBookServiceservice;// replaces the bean in the context@TestvoidreturnsBook(){given(service.byId(1)).willReturn(newBook(1,"Dune"));client.get().uri("/api/books/1").exchange().expectStatus().isOk().expectBody().jsonPath("$.title").isEqualTo("Dune");}}
New in Spring Boot 4
RestTestClient is the one fluent test client for both mock and running servers.
@MockBean is gone — use @MockitoBean (and @MockitoSpyBean).