Phase 6 — Spring + Spring Boot Backend
This phase takes you from knowing Java to being able to build a real Java backend application with Spring Boot.
The most important idea to keep in mind throughout this phase is:
Spring manages objects and their relationships for you, while Spring Boot makes it practical to build and run a Spring application quickly.
Topics in this Phase
Topic 1 — Spring Fundamentals
Why Spring exists
IoC — Inversion of Control
Dependency Injection
Spring Beans
Component scanning
@Component,@Service,@Repository,@ControllerConstructor injection
Bean scopes
Bean lifecycle
Configuration classes and
@Bean
Topic 2 — Spring Boot Fundamentals
Spring Boot
Project structure
Starters and dependencies
Auto-configuration
Application configuration
Profiles
Environment variables
Logging
Running and packaging a Spring Boot application
Topic 3 — Building REST APIs
HTTP and REST in Spring
@RestControllerRequest mappings
Path variables
Query parameters
Request bodies
Response bodies
HTTP status codes
ResponseEntity
Topic 4 — Backend Architecture
Controller layer
Service layer
Repository layer
DTOs
Entity vs DTO
Dependency flow
Separation of responsibilities
Topic 5 — Validation & Error Handling
Bean validation
@ValidValidation constraints
Validation error responses
Custom exceptions
Global exception handling
@ControllerAdviceConsistent API error responses
Topic 6 — Database Integration with Spring
Spring Data JPA
Repository interfaces
CRUD repositories
Query methods
Custom queries
Pagination
Sorting
Transactions
Service + repository integration
Topic 7 — Production REST API
Complete CRUD API
DTO mapping
Pagination/filtering
Configuration
Logging
Error handling
Validation
API documentation concepts
Topic 1 — Spring Fundamentals
Chapter 1 — Why Spring Exists
Question
Given below is a code snippet that:
Creates an application service.
Creates the dependency required by that service.
Demonstrates the problem Spring's Dependency Injection is designed to solve.
Uses constructor injection manually.
What should be the output of the following code?
// This class represents something our application needs.
class EmailService {
// This method sends an email.
void send(String message) {
System.out.println("Email: " + message);
}
}
// This class depends on EmailService.
class NotificationService {
private final EmailService emailService;
// The dependency is supplied through the constructor.
NotificationService(EmailService emailService) {
this.emailService = emailService;
}
// This method uses the dependency.
void notifyUser() {
emailService.send("Welcome!");
}
}
// The application starts here.
public class Main {
public static void main(String[] args) {
// We manually create the dependency.
EmailService emailService = new EmailService();
// We manually give the dependency to NotificationService.
NotificationService notificationService =
new NotificationService(emailService);
// The service uses its dependency.
notificationService.notifyUser();
}
}Answer
Email: Welcome!Step-by-step explanation
main()starts the program.new EmailService()creates anEmailServiceobject.That object is passed into
NotificationService.NotificationServicestores the object inemailService.notifyUser()callsemailService.send(...).The message is printed.
How to read the important code
"Create an EmailService and pass it into the NotificationService constructor."
This is dependency injection.
NotificationService needs an EmailService, so EmailService is its dependency.
Spring's job is largely to automate this object creation and dependency wiring.
Key takeaway
Spring's core purpose is to manage objects and their dependencies so your application doesn't have to manually create and connect everything.
Chapter 2 — Inversion of Control (IoC)
Question
Given below is a code snippet that:
Defines two Spring-managed components.
Allows Spring to create and manage the objects.
Demonstrates Inversion of Control.
Uses constructor injection.
What should be the output of the following code?
// Marks this class as an object Spring should manage.
@Component
class PaymentService {
void pay() {
System.out.println("Payment completed");
}
}
// Spring also manages this class.
@Component
class OrderService {
private final PaymentService paymentService;
// Spring supplies PaymentService here.
OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
void placeOrder() {
paymentService.pay();
System.out.println("Order placed");
}
}Answer
Payment completed
Order placedStep-by-step explanation
@Componenttells Spring that the class can become a Spring bean.Spring creates a
PaymentServiceobject.Spring creates an
OrderServiceobject.Spring notices that
OrderServiceneedsPaymentService.Spring supplies the dependency through the constructor.
placeOrder()callspay().Both messages are printed.
How to read the important code
"
OrderServicedepends onPaymentService, and Spring supplies that dependency."
Inversion of Control means your code is no longer completely responsible for controlling object creation.
Spring takes over part of that responsibility.
Key takeaway
IoC means control over object creation and wiring is transferred from your application code to the Spring container.
Chapter 3 — Dependency Injection
Question
Given below is a code snippet that:
Defines a dependency.
Injects it through a constructor.
Uses Spring's component scanning.
Demonstrates constructor-based Dependency Injection.
What should be the output of the following code?
@Component
class SmsService {
void sendSms() {
System.out.println("SMS sent");
}
}
@Service
class AlertService {
private final SmsService smsService;
// Spring injects SmsService into AlertService.
AlertService(SmsService smsService) {
this.smsService = smsService;
}
void alert() {
smsService.sendSms();
System.out.println("Alert completed");
}
}Answer
SMS sent
Alert completedStep-by-step explanation
SmsServiceis marked as a component.Spring creates it as a bean.
AlertServiceis marked as a service.Spring sees that its constructor requires
SmsService.Spring supplies the
SmsServicebean.alert()uses that object.
How to read the important code
"Create an AlertService whose constructor requires an SmsService."
Constructor injection is generally the preferred way to express required dependencies.
Key takeaway
Dependency Injection means an object receives the objects it needs instead of creating those objects itself.
Chapter 4 — Spring Beans
Question
Given below is a code snippet that:
Defines a Spring bean.
Retrieves the bean from the Spring container.
Demonstrates that Spring manages the object's lifecycle.
What should be the output of the following code?
@Component
class GreetingService {
void greet() {
System.out.println("Hello from Spring");
}
}
// Spring Boot creates the application context.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Start Spring and obtain its container.
ApplicationContext context =
SpringApplication.run(Application.class, args);
// Ask Spring for the managed GreetingService object.
GreetingService service =
context.getBean(GreetingService.class);
// Use the Spring-managed object.
service.greet();
}
}Answer
Hello from SpringStep-by-step explanation
SpringApplication.run()starts Spring.Spring scans the application.
It finds
GreetingService.Because of
@Component, Spring creates a bean.context.getBean(...)retrieves that managed object.greet()executes.
How to read the important code
"Start Spring, get the GreetingService bean from the application context, and call greet."
The ApplicationContext is Spring's container for managing beans.
Key takeaway
A Spring bean is an object whose creation and management are handled by Spring.
Chapter 5 — Component Scanning
Question
Given below is a code snippet that:
Marks a class as a Spring component.
Uses component scanning.
Demonstrates automatic bean discovery.
What should be the output of the following code?
// Spring discovers this class during component scanning.
@Component
class ProductService {
String getProductName() {
return "Laptop";
}
}
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Spring scans the application's package and finds ProductService.
ApplicationContext context =
SpringApplication.run(Application.class, args);
// Spring gives us the discovered bean.
ProductService service =
context.getBean(ProductService.class);
System.out.println(service.getProductName());
}
}Answer
LaptopStep-by-step explanation
Spring Boot starts.
Component scanning searches the relevant packages.
@ComponentidentifiesProductService.Spring creates the bean.
getBean()retrieves it.getProductName()returns"Laptop".
Key takeaway
Component scanning allows Spring to automatically discover classes that should become beans.
Chapter 6 — @Component, @Service, @Repository, @Controller
Question
Given below is a code snippet that:
Demonstrates Spring's specialized component annotations.
Separates controller, service, and repository responsibilities.
Shows the normal backend architecture.
What should be the output of the following code?
// Represents the data-access layer.
@Repository
class UserRepository {
String findUser() {
return "Alice";
}
}
// Represents business logic.
@Service
class UserService {
private final UserRepository repository;
UserService(UserRepository repository) {
this.repository = repository;
}
String getUser() {
return repository.findUser();
}
}
// Represents the HTTP/controller layer.
@Controller
class UserController {
private final UserService service;
UserController(UserService service) {
this.service = service;
}
void showUser() {
System.out.println(service.getUser());
}
}Answer
AliceStep-by-step explanation
@Repositoryidentifies the data-access component.@Serviceidentifies business logic.@Controlleridentifies the controller layer.Spring creates these objects.
Spring injects
UserRepositoryintoUserService.Spring injects
UserServiceintoUserController.The controller eventually retrieves
"Alice".
How to read the architecture
Controller → Service → Repository
The controller handles the outside request.
The service handles business logic.
The repository handles data access.
Key takeaway
These annotations communicate the role of each Spring-managed class and help organize a backend application.
Chapter 7 — Constructor Injection
Question
Given below is a code snippet that:
Uses constructor injection.
Demonstrates a required dependency.
Shows why the dependency can be safely stored in a
finalfield.
What should be the output of the following code?
@Component
class PriceService {
int getPrice() {
return 500;
}
}
@Service
class OrderService {
// The dependency cannot be replaced after construction.
private final PriceService priceService;
// Spring supplies PriceService here.
OrderService(PriceService priceService) {
this.priceService = priceService;
}
void printPrice() {
System.out.println(priceService.getPrice());
}
}Answer
500Step-by-step explanation
Spring creates
PriceService.Spring creates
OrderService.The constructor requires a
PriceService.Spring passes the bean into the constructor.
The reference is stored in the
finalfield.printPrice()callsgetPrice().
Beginner trap
Don't confuse:
private final PriceService priceService;with creating a new object.
It only declares a reference that will point to the dependency supplied by Spring.
Key takeaway
Prefer constructor injection for required dependencies.
Chapter 8 — Bean Scopes
Question
Given below is a code snippet that:
Retrieves the same singleton bean twice.
Compares the two references.
Demonstrates Spring's default bean scope.
What should be the output of the following code?
@Component
class CounterService {
// Each CounterService object has its own counter.
private int count = 0;
int increment() {
return ++count;
}
}
// Assume this code runs inside a Spring application.
class Demo {
void test(ApplicationContext context) {
// Ask Spring for the bean twice.
CounterService first =
context.getBean(CounterService.class);
CounterService second =
context.getBean(CounterService.class);
// Both references normally point to the same singleton bean.
System.out.println(first == second);
// The same object's state is changed.
System.out.println(first.increment());
System.out.println(second.increment());
}
}Answer
true
1
2Step-by-step explanation
Spring's default scope is singleton.
firstgets the managed bean.secondgets the same managed bean.Therefore
first == secondistrue.first.increment()changes the object's count to1.second.increment()accesses the same object and changes it to2.
Key takeaway
By default, Spring creates one bean instance per application context.
Chapter 9 — Bean Lifecycle
Question
Given below is a code snippet that:
Uses a bean initialization callback.
Uses a bean destruction callback.
Demonstrates the basic Spring bean lifecycle.
What should be the output order?
@Component
class DatabaseService {
// Runs after Spring creates and injects the bean.
@PostConstruct
void start() {
System.out.println("Database service started");
}
// Normal application method.
void query() {
System.out.println("Query executed");
}
// Runs when Spring destroys the bean.
@PreDestroy
void stop() {
System.out.println("Database service stopped");
}
}Answer
During application startup and shutdown:
Database service started
Query executed
Database service stoppedStep-by-step explanation
Spring creates the bean.
Dependencies are injected.
@PostConstructruns.The application uses the bean.
When the application context shuts down,
@PreDestroycan run.Cleanup happens.
Key takeaway
Spring manages the bean lifecycle from creation through destruction.
Chapter 10 — Configuration Classes and @Bean
Question
Given below is a code snippet that:
Creates a bean using
@Bean.Uses a configuration class.
Injects that bean into another service.
What should be the output of the following code?
@Configuration
class AppConfig {
// Spring calls this method and manages the returned object as a bean.
@Bean
PaymentGateway paymentGateway() {
return new PaymentGateway();
}
}
class PaymentGateway {
void charge() {
System.out.println("Payment charged");
}
}
@Service
class CheckoutService {
private final PaymentGateway gateway;
// Spring injects the PaymentGateway bean.
CheckoutService(PaymentGateway gateway) {
this.gateway = gateway;
}
void checkout() {
gateway.charge();
}
}Answer
Payment chargedStep-by-step explanation
@ConfigurationmarksAppConfigas configuration.@Beantells Spring that the returnedPaymentGatewayshould become a bean.Spring creates the
PaymentGateway.CheckoutServicerequires it.Spring injects it through the constructor.
checkout()callscharge().
Key takeaway
Use
@Beanwhen you want explicit control over how a Spring-managed object is created.
Topic 2 — Spring Boot Fundamentals
Chapter 11 — Spring Boot
Question
Given below is a code snippet that:
Starts a Spring Boot application.
Uses
@SpringBootApplication.Defines the application's entry point.
What should be the output?
// Combines important Spring Boot configuration features.
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Starts the Spring application and embedded server.
SpringApplication.run(Application.class, args);
// This line executes after startup begins.
System.out.println("Application started");
}
}Answer
Application startedStep-by-step explanation
Java starts
main().SpringApplication.run()starts Spring Boot.Spring creates the application context.
Spring performs configuration and bean setup.
The next statement prints the message.
Key takeaway
@SpringBootApplicationplusSpringApplication.run()is the standard starting point for a Spring Boot application.
Chapter 12 — Spring Boot Project Structure
Question
Given below is a code snippet that:
Shows a typical Spring Boot package structure.
Places the main application class at the package root.
Allows component scanning to discover application components.
What should be the output?
package com.example.shop;
// Main application class at the root package.
@SpringBootApplication
public class ShopApplication {
public static void main(String[] args) {
// Starts the Spring Boot application.
SpringApplication.run(ShopApplication.class, args);
System.out.println("Shop application running");
}
}Assume the application also contains:
com.example.shop
├── ShopApplication
├── controller
├── service
├── repository
└── dtoAnswer
Shop application runningStep-by-step explanation
Spring Boot starts from
ShopApplication.Component scanning begins around its package.
Subpackages such as
controller,service, andrepositorycan be discovered.Spring creates the appropriate beans.
The application starts.
Key takeaway
Package organization matters because Spring's component scanning depends on where your application starts scanning.
Chapter 13 — Starters and Dependencies
Question
Given below is a conceptual Spring Boot configuration that:
Adds the Web starter.
Allows Spring MVC and embedded-server functionality.
Demonstrates dependency-driven application features.
What should the application be able to do?
<!-- Spring Boot's web starter brings common web dependencies. -->
<dependency>
<!-- Identifies the Spring Boot Web starter. -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>And:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Starts the web application.
SpringApplication.run(Application.class, args);
System.out.println("Web application started");
}
}Answer
Web application startedStep-by-step explanation
The dependency tells the build system what functionality the application needs.
The Web starter brings common Spring web functionality.
Spring Boot configures that functionality.
The application can act as a web server.
Key takeaway
Spring Boot starters provide convenient groups of dependencies for common application types.
Chapter 14 — Auto-Configuration
Question
Given below is a code snippet that:
Starts a Spring Boot web application.
Relies on Spring Boot's automatic configuration.
Uses an embedded server.
What should happen?
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Spring Boot detects that web dependencies exist.
SpringApplication.run(Application.class, args);
System.out.println("Server application started");
}
}Answer
Server application startedStep-by-step explanation
Spring Boot examines the application's dependencies.
It detects web-related dependencies.
It automatically configures many required components.
An embedded web server is configured.
The application starts.
Key takeaway
Auto-configuration means Spring Boot automatically configures many things based on what your application contains.
Chapter 15 — Application Configuration
Question
Given below is a code snippet that:
Reads application configuration.
Uses a configurable application name.
Separates configuration from Java code.
Assume:
app.name=Shop APICode:
@Component
class AppInfo {
// Spring injects the configuration property value.
@Value("${app.name}")
private String name;
void print() {
System.out.println(name);
}
}Answer
Shop APIStep-by-step explanation
The configuration contains
app.name=Shop API.Spring reads the configuration.
@Value("${app.name}")asks Spring for that property.Spring assigns
"Shop API"toname.print()displays it.
Key takeaway
Configuration values should normally be kept outside hard-coded application logic.
Chapter 16 — Profiles
Question
Given below is a configuration that:
Defines different environments.
Uses a development profile.
Shows how Spring can select environment-specific configuration.
Assume:
# application.properties
spring.profiles.active=dev# application-dev.properties
app.message=Development Mode@Component
@Profile("dev")
class DevelopmentService {
void run() {
System.out.println("Development Mode");
}
}Answer
Development ModeStep-by-step explanation
Spring sees that the
devprofile is active.It loads development-specific configuration.
@Profile("dev")allowsDevelopmentServiceto be created.The service runs.
Key takeaway
Profiles let the same application use different configurations for development, testing, and production.
Chapter 17 — Environment Variables
Question
Given below is a code snippet that:
Reads a value from configuration.
Allows an environment variable to override it.
Demonstrates externalized configuration.
Assume:
server.port=${PORT:8080}and the environment variable is:
PORT=9090What port will Spring Boot use?
@SpringBootApplication
public class Application {
public static void main(String[] args) {
// Spring Boot reads server.port from configuration.
SpringApplication.run(Application.class, args);
System.out.println("Application configured");
}
}Answer
Application configuredThe application uses:
9090Step-by-step explanation
Spring reads
server.port.${PORT:8080}means "use thePORTenvironment variable if available."PORTis9090.Therefore Spring uses port
9090.8080is only the fallback.
Key takeaway
Environment variables are a common way to provide deployment-specific configuration without changing source code.
Chapter 18 — Logging
Question
Given below is a code snippet that:
Creates a logger.
Writes an informational message.
Uses logging instead of
System.out.println().
What should the message communicate?
@Service
class OrderService {
// Create a logger for this class.
private static final Logger log =
LoggerFactory.getLogger(OrderService.class);
void createOrder() {
// Write an informational log entry.
log.info("Creating new order");
System.out.println("Order created");
}
}Answer
Order createdThe application also produces a log entry containing:
Creating new orderStep-by-step explanation
Spring creates
OrderService.createOrder()runs.log.info(...)writes a structured application log.System.out.println()prints directly to standard output.
Beginner trap
In professional backend applications, don't use System.out.println() as your primary logging mechanism.
Key takeaway
Logging gives production applications a controllable way to record what the application is doing.
Topic 3 — REST APIs
Chapter 19 — @RestController
Question
Given below is a code snippet that:
Creates a REST controller.
Defines an HTTP endpoint.
Returns text as the response.
What should a request to GET /hello return?
// Marks this class as a REST API controller.
@RestController
class HelloController {
// Handles GET requests to /hello.
@GetMapping("/hello")
String hello() {
// The returned String becomes the HTTP response body.
return "Hello Java";
}
}Answer
Hello JavaStep-by-step explanation
A client sends
GET /hello.Spring finds
HelloController.@GetMapping("/hello")matches the request.hello()executes.Its return value becomes the response body.
Key takeaway
@RestControlleris used to create HTTP APIs whose method results become response data.
Chapter 20 — Request Mappings
Question
Given below is a code snippet that:
Defines GET and POST endpoints.
Maps different HTTP methods to different Java methods.
What should the two requests return?
@RestController
@RequestMapping("/products")
class ProductController {
// Handles GET /products.
@GetMapping
String list() {
return "Product list";
}
// Handles POST /products.
@PostMapping
String create() {
return "Product created";
}
}Answer
GET /products
→ Product list
POST /products
→ Product createdStep-by-step explanation
@RequestMapping("/products")creates the common URL prefix.@GetMappinghandles GET.@PostMappinghandles POST.Both use the same path but different HTTP methods.
Key takeaway
HTTP method + URL together determine which controller method handles a request.
Chapter 21 — Path Variables
Question
Given below is a code snippet that:
Reads an ID from the URL.
Uses
@PathVariable.Builds a response using that ID.
What should GET /users/42 return?
@RestController
class UserController {
// {id} is a variable part of the URL.
@GetMapping("/users/{id}")
String getUser(@PathVariable int id) {
// The URL value becomes the Java variable id.
return "User ID: " + id;
}
}Answer
User ID: 42Step-by-step explanation
The request is
/users/42.{id}matches42.Spring converts
42into anint.idtherefore contains42.The method returns the response.
Key takeaway
@PathVariableextracts a value embedded directly in the URL path.
Chapter 22 — Query Parameters
Question
Given below is a code snippet that:
Reads a query parameter.
Uses
@RequestParam.Provides a default value.
What should the two requests return?
@RestController
class ProductController {
// Reads the "page" query parameter.
@GetMapping("/products")
String products(
@RequestParam(defaultValue = "1") int page) {
return "Page: " + page;
}
}Requests:
GET /products?page=3
GET /productsAnswer
GET /products?page=3
→ Page: 3
GET /products
→ Page: 1Step-by-step explanation
page=3is supplied in the first request.Spring assigns
3topage.The second request doesn't provide
page.Spring therefore uses the default value
1.
Key takeaway
Query parameters are commonly used for filtering, pagination, sorting, and optional request information.
Chapter 23 — Request Bodies
Question
Given below is a code snippet that:
Accepts JSON.
Converts the JSON into a Java object.
Uses
@RequestBody.
Assume the client sends:
{
"name": "Laptop"
}What should the endpoint return?
// Represents data coming from the client.
class ProductRequest {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@RestController
class ProductController {
// Spring reads JSON from the request body.
@PostMapping("/products")
String create(@RequestBody ProductRequest request) {
// request.name contains "Laptop".
return "Created: " + request.getName();
}
}Answer
Created: LaptopStep-by-step explanation
The client sends JSON.
Spring's JSON support converts the JSON into
ProductRequest."name"becomes the Java object'snamefield.@RequestBodyprovides that object to the method.The method returns
"Created: Laptop".
Key takeaway
@RequestBodylets Spring convert incoming JSON into a Java object.
Chapter 24 — Response Bodies
Question
Given below is a code snippet that:
Returns a Java object from a REST endpoint.
Lets Spring serialize the object as JSON.
What should the HTTP response body look like?
class Product {
private int id;
private String name;
// Constructor initializes the product.
Product(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
}
@RestController
class ProductController {
// Spring converts the returned object into JSON.
@GetMapping("/product")
Product product() {
return new Product(1, "Laptop");
}
}Answer
Conceptually:
{
"id": 1,
"name": "Laptop"
}Step-by-step explanation
The controller returns a
Productobject.Spring's HTTP message conversion handles the object.
JSON serialization converts its data into JSON.
The client receives JSON.
Key takeaway
REST controllers commonly return Java objects that Spring serializes into JSON.
Chapter 25 — HTTP Status Codes
Question
Given below is a code snippet that:
Returns an HTTP
201 Createdstatus.Sends a response body.
Uses
ResponseEntity.
What should the client receive?
@RestController
class ProductController {
@PostMapping("/products")
ResponseEntity<String> create() {
// Build a response with HTTP 201.
return ResponseEntity
.status(HttpStatus.CREATED)
.body("Product created");
}
}Answer
HTTP status: 201 Created
Body:
Product createdStep-by-step explanation
The client sends POST.
create()executes.ResponseEntitylets us control the HTTP response.HttpStatus.CREATEDmeans201.The body contains
"Product created".
Key takeaway
HTTP status codes communicate the result of an API operation to the client.
Chapter 26 — ResponseEntity
Question
Given below is a code snippet that:
Returns different HTTP responses.
Uses
ResponseEntity.Demonstrates a successful and missing-resource response.
What should each request return?
@RestController
class UserController {
@GetMapping("/users/{id}")
ResponseEntity<String> getUser(@PathVariable int id) {
// User 1 exists.
if (id == 1) {
return ResponseEntity.ok("Alice");
}
// Other IDs are treated as missing.
return ResponseEntity.notFound().build();
}
}Answer
GET /users/1
→ HTTP 200
→ Alice
GET /users/2
→ HTTP 404
→ No response bodyStep-by-step explanation
The ID is extracted from the URL.
If it is
1, Spring returns200 OK.Otherwise it returns
404 Not Found.ResponseEntitygives the method control over the HTTP response.
Key takeaway
ResponseEntityis useful when an endpoint needs explicit control over status, headers, and body.
Topic 4 — Backend Architecture
Chapter 27 — Controller → Service → Repository
Question
Given below is a code snippet that:
Separates HTTP handling from business logic and data access.
Uses Controller → Service → Repository.
Demonstrates the basic Spring backend architecture.
What should the output be?
@Repository
class ProductRepository {
String findName() {
return "Laptop";
}
}
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
String getProduct() {
// Business layer asks repository for data.
return repository.findName();
}
}
@RestController
class ProductController {
private final ProductService service;
ProductController(ProductService service) {
this.service = service;
}
@GetMapping("/product")
String product() {
// Controller delegates to service.
return service.getProduct();
}
}Answer
LaptopStep-by-step explanation
HTTP request reaches the controller.
Controller calls the service.
Service calls the repository.
Repository returns the data.
Service returns it to controller.
Controller returns it to the client.
Key takeaway
A common Spring backend flow is Controller → Service → Repository.
Chapter 28 — DTOs
Question
Given below is a code snippet that:
Uses an internal domain object.
Creates a DTO for API output.
Prevents the API from directly exposing the internal object.
What should the endpoint return?
// Internal application object.
class User {
String username;
String password;
User(String username, String password) {
this.username = username;
this.password = password;
}
}
// Data Transfer Object sent to the client.
class UserResponse {
private final String username;
UserResponse(String username) {
this.username = username;
}
public String getUsername() {
return username;
}
}
@RestController
class UserController {
@GetMapping("/user")
UserResponse user() {
// Internal object contains sensitive information.
User user = new User("alice", "secret");
// DTO exposes only the information the API should return.
return new UserResponse(user.username);
}
}Answer
Conceptually:
{
"username": "alice"
}The password is not returned.
Step-by-step explanation
Usercontains both username and password.The API shouldn't expose the password.
UserResponsecontains only username.The controller creates a DTO.
Spring serializes the DTO into JSON.
Key takeaway
DTOs define what data crosses an application boundary instead of exposing internal objects directly.
Chapter 29 — Entity vs DTO
Question
Given below is a code snippet that:
Represents a database entity.
Converts it into an API DTO.
Keeps persistence and API models separate.
What should the response contain?
// Represents database data.
@Entity
class User {
@Id
private Long id;
private String username;
private String password;
// getters omitted for brevity
}
// Represents API response data.
class UserResponse {
private final Long id;
private final String username;
UserResponse(Long id, String username) {
this.id = id;
this.username = username;
}
public Long getId() {
return id;
}
public String getUsername() {
return username;
}
}Assume the database user contains:
id = 10
username = alice
password = secretAnswer
{
"id": 10,
"username": "alice"
}Step-by-step explanation
The entity represents persistence/database data.
The entity contains a password.
The DTO intentionally doesn't contain a password.
Therefore the API response doesn't expose it.
Key takeaway
Entity = persistence model. DTO = API/data-transfer model.
Chapter 30 — Separation of Responsibilities
Question
Given below is a code snippet that:
Keeps HTTP logic in the controller.
Keeps business logic in the service.
Keeps database access in the repository.
What should the output be?
@Repository
class AccountRepository {
int getBalance() {
return 1000;
}
}
@Service
class AccountService {
private final AccountRepository repository;
AccountService(AccountRepository repository) {
this.repository = repository;
}
String withdraw(int amount) {
int balance = repository.getBalance();
// Business rule belongs in the service.
if (amount > balance) {
return "Insufficient funds";
}
return "Withdrawal approved";
}
}
@RestController
class AccountController {
private final AccountService service;
AccountController(AccountService service) {
this.service = service;
}
@GetMapping("/withdraw")
String withdraw() {
// Controller handles the API request.
return service.withdraw(700);
}
}Answer
Withdrawal approvedStep-by-step explanation
Controller receives the request.
Controller calls the service.
Service asks repository for balance.
Balance is
1000.Requested withdrawal is
700.700is not greater than1000.Service approves it.
Controller returns the result.
Key takeaway
Keep each layer focused on its own responsibility.
Topic 5 — Validation & Error Handling
Chapter 31 — Bean Validation
Question
Given below is a code snippet that:
Defines validation rules.
Requires a username.
Requires an email format.
Uses
@Valid.
What happens when the request contains an empty username?
class UserRequest {
// Username cannot be empty.
@NotBlank
private String username;
// Value must have a valid email format.
@Email
private String email;
// getters and setters omitted
}
@RestController
class UserController {
@PostMapping("/users")
String create(@Valid @RequestBody UserRequest request) {
// This executes only after validation succeeds.
return "User created";
}
}Request:
{
"username": "",
"email": "alice@example.com"
}Answer
The controller method does not successfully create the user.
Spring detects the validation failure and returns a validation error response, typically with an HTTP 400 Bad Request unless customized.
Step-by-step explanation
JSON is converted into
UserRequest.@Validtells Spring to validate it.@NotBlankchecksusername.Username is empty.
Validation fails.
The controller method isn't allowed to continue normally.
Key takeaway
Validation prevents invalid data from entering your application logic.
Chapter 32 — Validation Constraints
Question
Given below is a code snippet that:
Requires a name.
Restricts age.
Validates an email address.
What happens to the following request?
class CustomerRequest {
// Name must contain something other than whitespace.
@NotBlank
private String name;
// Age must be at least 18.
@Min(18)
private int age;
// Must follow email format.
@Email
private String email;
// getters and setters omitted
}Request:
{
"name": "Alice",
"age": 16,
"email": "alice@example.com"
}Answer
Validation fails because:
age = 16
minimum allowed = 18Step-by-step explanation
Name passes
@NotBlank.Email passes
@Email.Age is checked against
@Min(18).16 < 18.Validation fails.
Key takeaway
Validation annotations let you express common input rules directly on request models.
Chapter 33 — Custom Exceptions
Question
Given below is a code snippet that:
Defines a custom exception.
Throws it when a user doesn't exist.
Demonstrates domain-specific error handling.
What happens when user ID 10 is requested?
// Represents a specific application problem.
class UserNotFoundException extends RuntimeException {
UserNotFoundException(String message) {
super(message);
}
}
@Service
class UserService {
String findUser(int id) {
// Assume only user 1 exists.
if (id != 1) {
throw new UserNotFoundException("User not found");
}
return "Alice";
}
}Answer
For:
findUser(10)the method throws:
UserNotFoundExceptionwith message:
User not foundStep-by-step explanation
idis10.The condition
id != 1is true.Java executes
throw.A
UserNotFoundExceptionobject is created.Normal execution stops and the exception propagates upward.
Key takeaway
Custom exceptions let your application represent meaningful business/application failures.
Chapter 34 — Global Exception Handling
Question
Given below is a code snippet that:
Handles a custom exception globally.
Converts the exception into an HTTP response.
Prevents every controller method from needing its own
try/catch.
What should GET /users/10 return?
class UserNotFoundException extends RuntimeException {
UserNotFoundException(String message) {
super(message);
}
}
@RestController
class UserController {
@GetMapping("/users/{id}")
String getUser(@PathVariable int id) {
// User 1 exists.
if (id != 1) {
throw new UserNotFoundException("User not found");
}
return "Alice";
}
}
// Handles the exception for controllers globally.
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
ResponseEntity<String> handle(UserNotFoundException ex) {
// Convert the exception into HTTP 404.
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(ex.getMessage());
}
}Answer
HTTP 404 Not Found
User not foundStep-by-step explanation
/users/10reaches the controller.User 10 doesn't exist.
The controller throws
UserNotFoundException.Spring searches for a matching exception handler.
GlobalExceptionHandlerhandles it.The client receives
404.
Key takeaway
Global exception handling gives your API consistent error responses without repeating error-handling code in every controller.
Chapter 35 — Consistent API Error Responses
Question
Given below is a code snippet that:
Creates a structured API error.
Returns status and message together.
Demonstrates a production-friendly error response shape.
What should the JSON response be?
record ApiError(
int status,
String message
) {}
@RestControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
ResponseEntity<ApiError> handle(UserNotFoundException ex) {
// Create a predictable error object.
ApiError error =
new ApiError(404, ex.getMessage());
// Return HTTP 404 and the structured body.
return ResponseEntity
.status(404)
.body(error);
}
}Assume the exception message is:
User not foundAnswer
{
"status": 404,
"message": "User not found"
}Step-by-step explanation
The exception reaches the global handler.
The handler creates an
ApiError.statusis404.messageis"User not found".Spring serializes the record to JSON.
Key takeaway
Professional APIs should return predictable error structures rather than random error formats.
Topic 6 — Spring Data JPA
Chapter 36 — Repository Interface
Question
Given below is a code snippet that:
Defines a JPA entity.
Creates a Spring Data repository.
Lets Spring provide database operations automatically.
What should repository.findById(1L) conceptually return?
@Entity
class Product {
@Id
private Long id;
private String name;
// getters and setters omitted
}
// Spring Data creates the implementation automatically.
@Repository
interface ProductRepository
extends JpaRepository<Product, Long> {
}Answer
Conceptually:
An Optional<Product>If product ID 1 exists, it contains that product.
Step-by-step explanation
Productis marked as an entity.idis its primary key.ProductRepositoryextendsJpaRepository.Spring Data creates the implementation.
You don't need to manually write basic SQL for CRUD operations.
Key takeaway
Spring Data JPA lets you perform common database operations through repository interfaces.
Chapter 37 — CRUD Operations
Question
Given below is a code snippet that:
Saves an entity.
Finds it by ID.
Uses Spring Data JPA's built-in methods.
What should the final output be?
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
void demo() {
// Create a product object.
Product product = new Product();
// Save it through the repository.
repository.save(product);
// Retrieve the product.
Optional<Product> result =
repository.findById(product.getId());
// Check whether it exists.
System.out.println(result.isPresent());
}
}Answer
Assuming the save succeeds and an ID is generated:
trueStep-by-step explanation
A
Productobject is created.save()persists it.The database assigns an ID if configured for generated IDs.
findById()searches for it.The result contains the product.
isPresent()returnstrue.
Key takeaway
Spring Data provides common persistence operations such as
save,findById,findAll, anddelete.
Chapter 38 — Query Methods
Question
Given below is a code snippet that:
Defines a repository query through a method name.
Searches products by name.
Demonstrates Spring Data's derived queries.
What does the repository method mean?
@Repository
interface ProductRepository
extends JpaRepository<Product, Long> {
// Spring derives the query from the method name.
List<Product> findByName(String name);
}If the database contains:
Laptop
Phone
Laptopwhat should:
findByName("Laptop")return?
Answer
2 productsBoth products whose name is:
LaptopStep-by-step explanation
Spring reads the repository method name.
findByNamemeans search using thenameproperty."Laptop"becomes the search value.All matching records are returned.
Key takeaway
Spring Data can generate many queries directly from repository method names.
Chapter 39 — Pagination
Question
Given below is a code snippet that:
Requests the second page.
Uses a page size of 10.
Demonstrates Spring Data pagination.
What page does Spring request?
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
Page<Product> getProducts() {
// Page numbers are zero-based.
Pageable pageable =
PageRequest.of(1, 10);
// Ask the database for that page.
return repository.findAll(pageable);
}
}Answer
Page number: 1
Page size: 10This is the second page because page numbering starts at zero.
Step-by-step explanation
PageRequest.of(1, 10)creates a pageable request.1means page index 1.10means ten records per page.Page index 0 would be the first page.
Therefore index 1 is the second page.
Beginner trap
Don't assume page 1 means the first page in Spring Data.
Spring Data pagination is zero-based.
Key takeaway
Pagination prevents APIs from loading huge datasets into memory at once.
Chapter 40 — Sorting
Question
Given below is a code snippet that:
Requests products sorted by price.
Uses Spring Data's
Sort.Demonstrates database-level sorting.
What order should the products be returned in?
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
List<Product> getProducts() {
// Sort products from lowest price to highest price.
Sort sort =
Sort.by("price").ascending();
// Ask the repository for sorted data.
return repository.findAll(sort);
}
}Assume prices are:
Laptop → 50000
Mouse → 500
Phone → 20000Answer
Mouse → 500
Phone → 20000
Laptop → 50000Step-by-step explanation
Sorting is based on
price..ascending()means smallest to largest.The database/repository returns the records in that order.
Key takeaway
Sorting can be delegated to the database rather than sorting large datasets inside Java memory.
Chapter 41 — Transactions
Question
Given below is a code snippet that:
Performs multiple database operations.
Uses one transaction.
Demonstrates rollback behavior.
What happens if the second operation throws an exception?
@Service
class TransferService {
private final AccountRepository repository;
TransferService(AccountRepository repository) {
this.repository = repository;
}
// Both database operations belong to one transaction.
@Transactional
void transfer() {
// Withdraw money.
repository.withdraw(500);
// An unexpected failure occurs.
throw new RuntimeException("Payment failure");
// This would not execute.
// repository.deposit(500);
}
}Answer
The transaction is rolled back.
Conceptually:
Withdraw → attempted
Exception → occurs
Transaction → rolled backStep-by-step explanation
The method starts inside a transaction.
The withdrawal occurs.
An unchecked exception is thrown.
The transaction is marked for rollback.
Database changes made in that transaction are rolled back.
Key takeaway
A transaction groups database operations so they can succeed or fail as one unit.
Chapter 42 — Service + Repository Integration
Question
Given below is a code snippet that:
Uses a repository to retrieve data.
Uses a service to apply business logic.
Uses a controller to expose the result.
What should GET /products/1 return?
@Repository
interface ProductRepository
extends JpaRepository<Product, Long> {
}
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
Product getProduct(Long id) {
// Search the database.
return repository.findById(id)
// Return the product if found.
.orElseThrow(() ->
new RuntimeException("Product not found"));
}
}
@RestController
class ProductController {
private final ProductService service;
ProductController(ProductService service) {
this.service = service;
}
@GetMapping("/products/{id}")
Product get(@PathVariable Long id) {
// Controller delegates to service.
return service.getProduct(id);
}
}Assume product 1 exists with name "Laptop".
Answer
Conceptually:
{
"id": 1,
"name": "Laptop"
}Step-by-step explanation
Request reaches the controller.
Controller extracts ID
1.Controller calls service.
Service calls repository.
Repository retrieves the entity.
Service returns it.
Controller returns it.
Spring converts it to JSON.
Key takeaway
The controller should normally coordinate the request, while the service handles application logic and the repository handles persistence.
Topic 7 — Production REST API
Chapter 43 — Complete CRUD API Flow
Question
Given below is a code snippet that:
Accepts a request.
Validates data.
Passes it to the service.
Saves through a repository.
Returns a response DTO.
What is the architectural flow?
record ProductRequest(
@NotBlank String name,
@Min(1) int price
) {}
record ProductResponse(
Long id,
String name,
int price
) {}
@Service
class ProductService {
private final ProductRepository repository;
ProductService(ProductRepository repository) {
this.repository = repository;
}
ProductResponse create(ProductRequest request) {
// Convert request data into an entity.
Product product = new Product();
product.setName(request.name());
product.setPrice(request.price());
// Save through Spring Data.
Product saved = repository.save(product);
// Convert entity into API response.
return new ProductResponse(
saved.getId(),
saved.getName(),
saved.getPrice()
);
}
}
@RestController
@RequestMapping("/products")
class ProductController {
private final ProductService service;
ProductController(ProductService service) {
this.service = service;
}
@PostMapping
ResponseEntity<ProductResponse> create(
@Valid @RequestBody ProductRequest request) {
// Controller delegates business work.
ProductResponse response =
service.create(request);
// Return HTTP 201 with the created product.
return ResponseEntity
.status(HttpStatus.CREATED)
.body(response);
}
}Assume the request is:
{
"name": "Laptop",
"price": 50000
}and the database assigns ID 7.
Answer
Conceptually:
HTTP 201 Created
{
"id": 7,
"name": "Laptop",
"price": 50000
}Step-by-step explanation
Client sends POST
/products.Spring converts JSON into
ProductRequest.@Validchecks the request.Controller calls
ProductService.Service creates a
Productentity.Repository saves it.
Database generates ID
7.Service converts the entity into
ProductResponse.Controller returns
201 Created.Spring converts the DTO into JSON.
Key takeaway
This is the basic shape of a real Spring Boot CRUD endpoint.
Chapter 44 — DTO Mapping
Question
Given below is a code snippet that:
Converts a request DTO into an entity.
Saves the entity.
Converts the entity into a response DTO.
Keeps API models separate from database models.
What should the response contain?
record ProductRequest(String name, int price) {}
record ProductResponse(Long id, String name, int price) {}
@Service
class ProductService {
ProductResponse create(ProductRequest request) {
// Convert API request into database entity.
Product product = new Product();
product.setName(request.name());
product.setPrice(request.price());
// Pretend the database assigned ID 10.
product.setId(10L);
// Convert entity into API response.
return new ProductResponse(
product.getId(),
product.getName(),
product.getPrice()
);
}
}Answer
{
"id": 10,
"name": "Laptop",
"price": 50000
}Step-by-step explanation
Request DTO contains client data.
Service converts it to an entity.
Entity receives database ID
10.Service converts entity into response DTO.
API exposes the DTO.
Key takeaway
Mapping between DTOs and entities is a normal part of a well-structured backend.
Chapter 45 — Pagination + Sorting + Filtering
Question
Given below is a code snippet that:
Accepts page information.
Accepts sorting.
Accepts a search term.
Demonstrates the shape of a production-style listing endpoint.
What values does the service receive for this request?
@RestController
class ProductController {
@GetMapping("/products")
String products(
// Page number supplied by client.
@RequestParam(defaultValue = "0") int page,
// Number of records per page.
@RequestParam(defaultValue = "10") int size,
// Optional search text.
@RequestParam(required = false) String search,
// Sorting field.
@RequestParam(defaultValue = "name") String sort) {
return "page=" + page
+ ", size=" + size
+ ", search=" + search
+ ", sort=" + sort;
}
}Request:
GET /products?page=2&size=20&search=laptop&sort=priceAnswer
page=2, size=20, search=laptop, sort=priceStep-by-step explanation
page=2becomespage.size=20becomessize.search=laptopbecomessearch.sort=pricebecomessort.The service layer could then convert these values into a database query.
Key takeaway
Real APIs commonly combine pagination, filtering, and sorting.
Chapter 46 — Configuration + Service
Question
Given below is a code snippet that:
Reads application configuration.
Injects it into a service.
Uses configuration rather than hard-coded values.
Assume:
app.discount=10What should the output be?
@Service
class PricingService {
// Spring injects the configured discount.
@Value("${app.discount}")
private int discount;
int calculate(int price) {
// Apply the configured discount percentage.
return price - (price * discount / 100);
}
}
@RestController
class PricingController {
private final PricingService service;
PricingController(PricingService service) {
this.service = service;
}
@GetMapping("/price")
int price() {
// Calculate discounted price.
return service.calculate(1000);
}
}Answer
900Step-by-step explanation
Configuration says discount is
10.Spring injects
10intodiscount.Price is
1000.Discount is
100.Final price is
900.
Key takeaway
Application behavior that varies between environments should generally be configurable.
Chapter 47 — Logging in a Service
Question
Given below is a code snippet that:
Logs an important business operation.
Uses structured logging placeholders.
Returns the result.
What should the method return?
@Service
class OrderService {
private static final Logger log =
LoggerFactory.getLogger(OrderService.class);
int createOrder(int userId, int amount) {
// Log useful business information.
log.info(
"Creating order for userId={}, amount={}",
userId,
amount
);
// Pretend the database generated this ID.
int orderId = 1001;
// Log the result.
log.info("Order created with id={}", orderId);
return orderId;
}
}Answer
1001Step-by-step explanation
userIdis logged.amountis logged.The order ID is generated.
The new ID is logged.
The method returns
1001.
Beginner trap
Don't log secrets such as:
passwords
JWT tokens
credit-card numbersKey takeaway
Logs should help developers understand application behavior without exposing sensitive information.
Chapter 48 — API Documentation Concept
Question
Given below is a REST endpoint that:
Has a clear HTTP method.
Has a clear resource URL.
Accepts a request.
Returns a response.
What should this endpoint represent?
@RestController
@RequestMapping("/users")
class UserController {
// GET /users/42
@GetMapping("/{id}")
UserResponse getUser(
@PathVariable Long id) {
// The service would normally retrieve the user.
return new UserResponse(
id,
"Alice"
);
}
}Answer
The endpoint represents:
GET /users/{id}Example:
GET /users/42Response:
{
"id": 42,
"username": "Alice"
}Step-by-step explanation
/usersrepresents the user resource.{id}identifies one specific user.GET means retrieve data.
The response contains the user representation.
Key takeaway
Good REST APIs make their resources, operations, inputs, and outputs predictable.
Phase 6 — What You Should Now Understand
After completing this phase, you should be able to look at a Spring Boot application and understand the major flow:
CLIENT
│
│ HTTP
▼
┌──────────────────┐
│ CONTROLLER │
│ │
│ HTTP handling │
│ Request/Response │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ SERVICE │
│ │
│ Business Logic │
└────────┬─────────┘
│
▼
┌──────────────────┐
│ REPOSITORY │
│ │
│ Database access │
└────────┬─────────┘
│
▼
DATABASEAnd the Spring side looks like:
Spring Boot
│
▼
ApplicationContext
│
├── Controller Bean
│ │
│ ▼
├── Service Bean
│ │
│ ▼
├── Repository Bean
│ │
│ ▼
└── Other BeansThe most important concepts you've covered in Phase 6 are:
IoC
Dependency Injection
Spring Beans
ApplicationContext
Component scanning
Constructor injection
Bean scopes
Bean lifecycle
@BeanconfigurationSpring Boot
Auto-configuration
Starters
Configuration
Profiles
Environment variables
Logging
REST controllers
HTTP mappings
Path variables
Query parameters
Request bodies
JSON responses
HTTP status codes
ResponseEntityController/Service/Repository architecture
DTOs
Validation
Custom exceptions
Global exception handling
Spring Data JPA
Repositories
CRUD
Derived queries
Pagination
Sorting
Transactions
Entity/DTO separation
Production-style REST API structure
The mental model to remember
Spring manages the objects.
Spring Boot configures the application.
Controllers handle HTTP.
Services handle business logic.
Repositories handle persistence.
DTOs control what crosses the API boundary.
Validation protects your application from bad input.
Exceptions represent failures.
Global handlers turn failures into consistent HTTP responses.
Spring Data JPA connects your service layer to the database.
This completes Phase 6 — Spring + Spring Boot Backend.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.