Interview question01 / 100
Practice session
Spring Boot
One hundred practical interview questions on auto-configuration, web APIs, data access, security basics, Actuator, and shipping Boot services — aimed at junior to mid-level developers.
100 curated questions10 coding exercisesQuestions tab: swipe cards or keyboard
Practice for Spring Boot
Progress
1 / 100
Arrow keys to move · Space or Enter to revealOn touch: swipe the card right or left
Hands-on
Coding exercises
Read the scenario, sketch your solution in the editor, reveal hints only when you need them, then compare against our reference implementation. Exercises run from beginner through mid-level.
In this track
10
scenarios
Minimal REST controller
Scenario
Create a `@RestController` mapped under `/api` with a GET endpoint `/api/hello` that returns plain text `"Hello, Spring"` and HTTP 200.
Use `@GetMapping` (or `@RequestMapping(method = GET)`). Assume the class lives in your application’s primary package so component scanning picks it up.
- Spring Boot 3.x with `jakarta.*` APIs.
- Return type can be `String`; Spring writes the body as text/plain.
Query parameter with a default
Scenario
Expose GET `/api/greet?name=...` that returns a JSON object (or plain string—reference uses a `record`) containing a greeting like `"Hello, Ada"`.
If `name` is omitted, default to `"World"`. Use `@RequestParam` with `defaultValue`.
Return type can be `record Greeting(String message)` for automatic JSON serialization.
- Use @RequestParam(name = "name", defaultValue = "World") String name.
POST JSON with validation
Scenario
Add POST `/api/books` accepting JSON `{ "title": "...", "pages": 200 }`.
• Use a request DTO with `jakarta.validation.constraints.NotBlank` on `title` and `@Min(1)` on `pages`.
• Annotate the body parameter with `@Valid` so validation runs automatically.
• Return `201 Created` with the same data echoed back (or an id)—reference echoes a `BookResponse` record.
Validation failures should return **400** with Spring’s default error body (no need for custom handler in this exercise).
- Include spring-boot-starter-validation on the classpath.
- Use records or POJOs with getters for the DTO.
Path variable and 404
Scenario
Implement GET `/api/users/{id}` where `id` is a positive long. If `id` is less than 1, return **404 Not Found** with an empty body or a small error object.
If `id` is valid, return **200** with JSON `{ "id": <id>, "name": "User " + id }` (mock data is fine—no database).
Use `ResponseEntity` to control status codes.
- Use @PathVariable Long id.
- 404 via ResponseEntity.notFound().build() or .status(HttpStatus.NOT_FOUND).
Type-safe configuration properties
Scenario
Bind settings from `application.properties` (or YAML) using `@ConfigurationProperties`.
Prefix: `app`. Properties: `app.name` and `app.version`.
Create a `record AppInfo(String name, String version)` annotated with `@ConfigurationProperties(prefix = "app")`.
Add a small `@Configuration` class that is **not** public (package-private) enabling the record: `@EnableConfigurationProperties(AppInfo.class)`. Put the **public** `AppInfo` record in the same file so the filename is `AppInfo.java` (the public type).
Expose a read-only GET `/api/app-info` that returns the bound `AppInfo` as JSON (inject `AppInfo` into a `@RestController`).
- Use constructor binding compatible with records (Spring Boot 3).
- Register EnableConfigurationProperties so the record is a bean.
Global exception handling
Scenario
Create a custom runtime exception `ResourceNotFoundException` with a `String resourceId` field and getter.
Add a `@RestControllerAdvice` class with:
• `@ExceptionHandler(ResourceNotFoundException.class)` returning **404** and a small JSON body like `{ "error": "NOT_FOUND", "id": "..." }`.
• `@ExceptionHandler(MethodArgumentNotValidException.class)` returning **400** and a map of field → default message for the first error (or all errors—reference returns first field error only).
Use `ResponseEntity` + `ProblemDetail` **or** a simple `Map` body—reference uses a `record ErrorBody` for consistency.
- Use org.springframework.web.bind.MethodArgumentNotValidException.
- Keep handler methods in one advice class.
Service layer with @Transactional
Scenario
Write an `OrderService` bean with a method `Optional<OrderView> findOrder(Long id)` that loads an order from an `OrderRepository` (Spring Data JPA).
• Annotate the service class with `@Service` and the read method with `@Transactional(readOnly = true)`.
• Define `Order` as a JPA `@Entity` with `Long id` (generated) and `String orderNumber`.
• Define `OrderRepository extends JpaRepository<Order, Long>`.
• Map the entity to `OrderView` record in the service.
Use **package-private** entity and repository in the same file as **public** `OrderService`, or split files—reference keeps one file with one `public` class `OrderService` and package-private types (valid Java). Actually **only one public top-level class per file**—so `OrderService` is public; `Order`, `OrderRepository`, `OrderView` are package-private in `OrderService.java`—but `OrderRepository` must be an interface for Spring Data—in same file package-private interface works.
**Note:** In real projects split packages; for the exercise a single snippet is enough.
- Enable JPA and a datasource (H2 in-memory is typical for demos).
- Use jakarta.persistence.* imports.
Entity + derived query method
Scenario
Model a `Product` entity with:
• `Long id` (generated identity)
• `String sku` (unique, not null)
• `String name`
Create `ProductRepository extends JpaRepository<Product, Long>` with a **derived** query method: `Optional<Product> findBySku(String sku)`.
No service layer required—reference solution is two files: `Product.java` and `ProductRepository.java` so package layout matches typical Spring Boot structure.
- Use @Column(nullable = false, unique = true) on sku.
- Constructors/getters as needed for JPA.
RestClient call to a remote API
Scenario
Spring Boot 3.2+ provides `RestClient` (and `RestClient.Builder` bean). Create a `@Service` `PostTitleClient` with a method `String fetchFirstPostTitle()` that GETs `https://jsonplaceholder.typicode.com/posts/1` and returns the `title` field from the JSON response.
Use a `record Post(String title)` for deserialization with `RestClient` and `.retrieve().body(Post.class)`, or `Map` if you prefer.
Inject `RestClient.Builder` in the constructor and `builder.baseUrl("https://jsonplaceholder.typicode.com").build()`, or use the full URL in `.get().uri("/posts/1")`.
- Add spring-boot-starter-web (includes RestClient support in Boot 3.2+).
- Handle only the happy path; exceptions can propagate for this drill.
Scheduled task + @EnableScheduling
Scenario
Add a `@Component` bean with a method annotated `@Scheduled(fixedRateString = "60000")` (every 60 seconds) that increments an `AtomicLong` counter and optionally logs the value (logging statement is enough to show intent).
Enable scheduling on the main application class with `@EnableScheduling`.
Expose the current counter value via GET `/api/metrics/tasks` returning JSON `{ "tickCount": <n> }` from a small `@RestController` that uses the same component bean.
**Note:** Combine **public** types carefully: use **one** public top-level class per file—reference uses `ScheduledHeartbeat.java` with a public `@Component` class and a **package-private** `@RestController` in the same file (valid), or split into two files in your repo.
- Use java.util.concurrent.atomic.AtomicLong.
- Do not block the scheduler thread for long periods.