Skip to content

Spring Boot Developer Interview Questions & Answers

40 questions for Spring Boot Developer interviews — 5 easy, 29 medium, 6 hard.

About the Spring Boot Developer role

A Spring Boot developer specialises in building microservices with the Spring ecosystem: Spring MVC, Spring Data, Spring Security and Spring Cloud. Daily work includes configuring services, securing APIs with JWT or OAuth2, adding observability with Actuator and Micrometer, and shipping through Docker and CI/CD pipelines. Companies moving from monoliths to microservices hire heavily for this role. Strong debugging habits and clear documentation are valued in this role.

Spring Boot Spring Security Microservices Docker Kafka
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
3 questions free Unlock the rest in seconds
01

Write code that runs 10,000 blocking tasks using virtual threads.

Save to ▾
Show answer
Use the virtual-thread-per-task executor in Java 21. try (var ex = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 10_000; i++) { int id = i; ex.submit(() -> { Thread.sleep(Duration.ofMillis(200)); return id; }); } } // close() waits for all tasks All tasks finish in about 200 ms because blocked virtual threads free their carriers.
02

A @Transactional method is not rolling back after an exception. What could be wrong?

Save to ▾
Show answer
Check that the exception is unchecked, since checked exceptions do not roll back unless rollbackFor is set. Check that the method is called through the Spring proxy, not from another method in the same class. Make sure the method is public and the exception is not caught and swallowed inside. Also confirm the database engine supports transactions and the right transaction manager is used.
03

Map a one-to-many relation between Order and OrderItem in JPA.

Save to ▾
Show answer
Make the child own the foreign key and keep both sides in sync. @Entity class Order { @Id @GeneratedValue Long id; @OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true) List<OrderItem> items = new ArrayList<>(); void addItem(OrderItem i) { items.add(i); i.setOrder(this); } } @Entity class OrderItem { @Id @GeneratedValue Long id; @ManyToOne(fetch = FetchType.LAZY) Order order; }
04

Describe a time you had to learn a new technology quickly.

Save to ▾
Show answer
Situation: I joined a project using reactive Spring WebFlux with no prior experience. Task: contribute within two weeks. Action: I studied Project Reactor basics, built a small practice service, paired with a senior on my first ticket and kept notes on common operators. Result: I delivered my first feature in the second week and later wrote the team guide on avoiding blocking calls in reactive code.
05

An API listing 100 orders fires 101 SQL queries. How do you fix it?

Save to ▾
Show answer
This is the N+1 problem from a lazy association loaded in a loop or by JSON serialisation. Fetch what the endpoint needs in one query with JOIN FETCH or @EntityGraph(attributePaths = "items"), or use a DTO projection query. For collections with pagination, fetch ids first, then fetch associations in a second query to avoid in-memory paging. Add a test that asserts the query count.
06

Users get LazyInitializationException when the API returns an entity. What is happening?

Save to ▾
Show answer
The entity was loaded inside a transaction, the session closed, and then Jackson touched a lazy association outside it. Do not fix it by turning on open-session-in-view or making everything eager. Instead, map the entity to a DTO inside the service transaction and fetch needed associations explicitly with JOIN FETCH or an entity graph. Spring Boot logs a warning because open-in-view is on by default.
07

You must expose configuration that differs between dev, QA and production. How do you manage it?

Save to ▾
Show answer
Use Spring profiles with application-dev.yml and application-prod.yml, and bind settings to typed @ConfigurationProperties classes with validation. Keep secrets out of the repo by injecting them through environment variables, Kubernetes Secrets or a vault. Activate the profile with SPRING_PROFILES_ACTIVE in each environment. Use the same build artefact everywhere and change only configuration.
08

What is the difference between synchronized and ReentrantLock?

Save to ▾
Show answer
synchronized is a built-in keyword that locks a monitor and releases it automatically at block exit. ReentrantLock is an explicit lock that adds tryLock with timeout, interruptible locking, fairness options and multiple Condition objects, but you must unlock in a finally block. Both are reentrant. Prefer synchronized for simple cases and ReentrantLock when you need its extra features.
09

Why should we hire you as a Java backend developer?

Save to ▾
Show answer
Situation: in my current role I build Spring Boot services for a payments product. Task: show impact beyond writing code. Action: I improved API latency by 60 percent through query tuning and caching, raised test coverage from 40 to 80 percent and handled on-call calmly. Result: I bring solid Java, Spring and SQL skills, ownership of production and a habit of measuring results, which fits this role.
10

Why is PreparedStatement preferred over Statement?

Save to ▾
Show answer
PreparedStatement sends the SQL with placeholders separately from the parameter values, so user input is never treated as SQL, which prevents SQL injection. The database can also cache and reuse the execution plan, and it handles types like dates correctly. Statement with string concatenation is unsafe and slower for repeated queries, so it should not be used with user input.
11

Clients report duplicate orders when they retry after a timeout. How do you prevent it?

Save to ▾
Show answer
Make the create order API idempotent. The client sends an Idempotency-Key header; the server stores the key with the result in a table with a unique constraint or in Redis. A retry with the same key returns the saved response instead of creating a new order. Database unique constraints on business keys add another safety net.
12

What is dependency injection and why is constructor injection preferred?

Save to ▾
Show answer
Dependency injection means the container creates objects and supplies their dependencies, instead of classes creating them, which reduces coupling and helps testing. Constructor injection is preferred because dependencies can be final, objects are never half-built, required dependencies are obvious and unit tests can pass mocks without Spring. Field injection with @Autowired hides dependencies and is harder to test.
13

A counter shared between threads shows wrong totals. How do you fix it?

Save to ▾
Show answer
count++ is not atomic; two threads can read the same value and both write the same result, so updates are lost. Use AtomicInteger.incrementAndGet(), which uses CAS, or LongAdder for very high contention, as it spreads updates over cells. A synchronized method also works but is slower. volatile alone does not fix it because it gives visibility, not atomicity.
14

What are virtual threads and when should you use them?

Save to ▾
Show answer
Virtual threads, final in Java 21, are lightweight threads managed by the JVM and mounted on a small pool of carrier OS threads. When one blocks on I/O, it unmounts so the carrier can run another. You can run millions of them, which suits thread-per-request servers doing lots of blocking calls. They do not speed up CPU-bound work, and you should not pool them.
15

A stream pipeline that updates a shared ArrayList in forEach gives random results with parallel(). Why?

Save to ▾
Show answer
ArrayList is not thread safe, and parallel streams run forEach on many threads, so additions race, get lost or throw exceptions. Side effects in streams are the problem. Replace forEach with collect(Collectors.toList()) or toList(), which handles merging per thread safely. Keep lambdas stateless and avoid modifying external state inside stream operations.
16

How does ExecutorService work and why use a thread pool?

Save to ▾
Show answer
ExecutorService manages a pool of worker threads and a task queue. You submit Runnable or Callable tasks and get Futures, and workers reuse threads instead of creating one per task, which saves creation cost and limits concurrency. Configure core size, max size, queue and rejection policy through ThreadPoolExecutor. Always call shutdown() and awaitTermination() when done.
17

Write a simple REST controller in Spring Boot for creating and fetching products.

Save to ▾
Show answer
Use constructor injection and validation. @RestController @RequestMapping("/api/products") class ProductController { private final ProductService service; ProductController(ProductService service) { this.service = service; } @PostMapping ResponseEntity<Product> create(@Valid @RequestBody ProductRequest req) { Product p = service.create(req); return ResponseEntity.created(URI.create("/api/products/" + p.id())).body(p); } @GetMapping("/{id}") Product get(@PathVariable Long id) { return service.get(id); } }
18

Explain the entity lifecycle states in JPA.

Save to ▾
Show answer
Transient: a new object not yet linked to a persistence context. Managed (persistent): attached to the context, so changes are tracked and flushed automatically through dirty checking. Detached: was managed but the context closed or it was evicted; changes are not tracked until merge(). Removed: scheduled for deletion on flush. persist, merge, detach and remove move entities between these states.
19

What is a deadlock and how can it be prevented?

Save to ▾
Show answer
A deadlock happens when two or more threads each hold a lock and wait for a lock held by the other, so none can continue. It needs mutual exclusion, hold and wait, no preemption and circular wait. Prevent it by always acquiring locks in the same global order, using tryLock with a timeout, holding locks briefly and avoiding calls to unknown code while holding a lock.
20

Your Spring Boot service takes 40 seconds to start in Kubernetes and pods get killed. How do you fix it?

Save to ▾
Show answer
First set a startupProbe so Kubernetes waits for startup before liveness checks begin. Then reduce start time: remove unused starters, enable lazy initialisation where safe, avoid heavy work in @PostConstruct, give enough CPU at start and consider AppCDS. For very fast startup, a GraalVM native image built with Spring AOT can start in under a second, with some trade-offs.
21

Write a Spring Data JPA repository method that fetches orders with items to avoid N+1.

Save to ▾
Show answer
Use JOIN FETCH or an entity graph. public interface OrderRepository extends JpaRepository<Order, Long> { @Query("select distinct o from Order o join fetch o.items where o.customerId = :cid") List<Order> findWithItems(@Param("cid") Long customerId); @EntityGraph(attributePaths = "items") List<Order> findByStatus(String status); } Both load orders and items in one query.
22

How do you mentor junior developers on your team?

Save to ▾
Show answer
Situation: three freshers joined my Spring Boot team at once. Task: make them productive quickly. Action: I created a starter task list, paired with each on their first story, gave detailed but kind code reviews and held a weekly session on topics like transactions and testing. Result: all three shipped features independently within a month and one now mentors new joiners.
23

Tell me about a microservice you designed end to end.

Save to ▾
Show answer
Situation: our monolith's notification code slowed every release. Task: extract it into its own service. Action: I designed a Spring Boot service consuming Kafka events, with its own database for templates and delivery logs, retries with backoff, idempotent consumers and Actuator metrics in Grafana. Result: it handles 20 lakh messages a day, deploys independently and notification incidents dropped sharply.
24

How does Spring Boot auto-configuration work?

Save to ▾
Show answer
At startup Spring Boot reads auto-configuration classes listed in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. Each class uses conditions like @ConditionalOnClass, @ConditionalOnMissingBean and @ConditionalOnProperty, so it creates beans only when the library is on the classpath and you have not defined your own. Run with --debug to see the condition report showing what was applied and why.
25

How would you write a simple thread safe rate limiter in Java?

Save to ▾
Show answer
A token bucket with synchronized refill is simple and correct. class RateLimiter { private final long capacity, refillPerSec; private double tokens; private long last = System.nanoTime(); RateLimiter(long cap, long rps) { capacity = cap; refillPerSec = rps; tokens = cap; } synchronized boolean tryAcquire() { long now = System.nanoTime(); tokens = Math.min(capacity, tokens + (now - last) / 1e9 * refillPerSec); last = now; if (tokens >= 1) { tokens--; return true; } return false; } } Across servers, keep the bucket in Redis.
26

How do you take a thread dump and a heap dump of a running Java process?

Save to ▾
Show answer
Find the process id with jps or ps, then use jcmd, which is the recommended tool. jps -l jcmd <pid> Thread.print > threads.txt jcmd <pid> GC.heap_dump /tmp/heap.hprof Older tools jstack and jmap do the same. Take three thread dumps a few seconds apart to spot stuck threads, and analyse heap dumps in Eclipse MAT.
27

Write a JDBC batch insert for a list of products.

Save to ▾
Show answer
Batching sends many rows in one round trip. String sql = "INSERT INTO product(name, price) VALUES (?, ?)"; try (Connection c = ds.getConnection(); PreparedStatement ps = c.prepareStatement(sql)) { c.setAutoCommit(false); int n = 0; for (Product p : products) { ps.setString(1, p.name()); ps.setLong(2, p.price()); ps.addBatch(); if (++n % 500 == 0) ps.executeBatch(); } ps.executeBatch(); c.commit(); }
28

Run two tasks in parallel with CompletableFuture and combine their results.

Save to ▾
Show answer
Use a dedicated executor for I/O work. ExecutorService pool = Executors.newFixedThreadPool(4); CompletableFuture<User> user = CompletableFuture.supplyAsync(() -> userApi.get(id), pool); CompletableFuture<List<Order>> orders = CompletableFuture.supplyAsync(() -> orderApi.list(id), pool); Profile p = user.thenCombine(orders, Profile::new) .orTimeout(2, TimeUnit.SECONDS) .join();
29

Two scheduled jobs on different servers process the same records twice. How do you prevent this?

Save to ▾
Show answer
In-JVM locks do not work across servers. Use a distributed lock such as ShedLock with a database table or Redis, so only one instance runs the job at a time. Make the processing idempotent too, for example by marking records with a status and updating with WHERE status = 'NEW' so only one worker claims each row. Database SELECT FOR UPDATE SKIP LOCKED also works well.
30

What is the difference between @Component, @Service, @Repository and @Controller?

Save to ▾
Show answer
All four mark a class as a Spring bean for component scanning. @Service and @Component are functionally the same, with @Service showing business logic intent. @Repository also enables translation of persistence exceptions into Spring DataAccessException. @Controller handles web requests and returns views, and @RestController adds @ResponseBody so return values are written as JSON.
31

What is the N+1 select problem in Hibernate and how do you fix it?

Save to ▾
Show answer
N+1 happens when you load N parent entities with one query and then a lazy association triggers one extra query per parent, giving N+1 queries. Fix it with a JOIN FETCH in JPQL, an @EntityGraph on the repository method, or batch fetching with @BatchSize or hibernate.default_batch_fetch_size. Detect it by enabling SQL logging or using a query count assertion in tests.
32

How would you split a monolith into microservices?

Save to ▾
Show answer
Do not split everything at once. Identify bounded contexts with the business, such as orders, payments and catalogue, and pick a module with clear boundaries and high change rate first. Use the strangler fig pattern: route its traffic through a gateway to the new service, give it its own database and sync data through events. Add observability and CI/CD before scaling out further.
33

Write a unit test for a service class using JUnit 5 and Mockito.

Save to ▾
Show answer
Mock the repository and verify behaviour. @ExtendWith(MockitoExtension.class) class ProductServiceTest { @Mock ProductRepository repo; @InjectMocks ProductService service; @Test void throwsWhenMissing() { when(repo.findById(1L)).thenReturn(Optional.empty()); assertThrows(NotFoundException.class, () -> service.get(1L)); verify(repo).findById(1L); } } No Spring context starts, so the test runs in milliseconds.
34

What is the difference between lazy and eager loading?

Save to ▾
Show answer
Lazy loading fetches an association only when it is first accessed, using a proxy; eager loading fetches it immediately with the parent. In JPA, @ManyToOne and @OneToOne default to EAGER, while @OneToMany and @ManyToMany default to LAZY. Best practice is to make all associations lazy and fetch what each use case needs with JOIN FETCH or entity graphs, which avoids loading huge object graphs.
35

Explain bean scopes in Spring.

Save to ▾
Show answer
singleton is the default: one instance per application context. prototype creates a new instance each time it is requested. Web scopes include request, session and application. Singletons must be stateless or thread safe because all requests share them. Injecting a prototype into a singleton gives only one instance, so use ObjectProvider or a scoped proxy to get fresh ones.
36

Bind typed configuration properties with validation in Spring Boot.

Save to ▾
Show answer
Use a record with @ConfigurationProperties. @ConfigurationProperties(prefix = "payment") @Validated public record PaymentProps( @NotBlank String baseUrl, @Min(100) int timeoutMs) {} // application.yml // payment: // base-url: https://pay.internal // timeout-ms: 2000 Enable it with @ConfigurationPropertiesScan on the main class. The app fails at startup if values are invalid.
37

What is exception chaining?

Save to ▾
Show answer
Exception chaining wraps a low level exception in a higher level one while keeping the original as the cause, for example throw new OrderException("save failed", sqlEx). The full stack trace shows both, with Caused by lines. It lets each layer speak its own language without losing the root cause, which is essential when debugging production logs.
38

Write a JPQL query that returns a DTO projection instead of entities.

Save to ▾
Show answer
Projections load only needed columns and avoid dirty checking. public record OrderSummary(Long id, String customer, long total) {} @Query(""" select new com.shop.OrderSummary(o.id, c.name, o.total) from Order o join o.customer c where o.createdAt >= :from """) List<OrderSummary> summaries(@Param("from") LocalDate from); Interface-based projections in Spring Data are another option.
39

Implement a producer-consumer using BlockingQueue.

Save to ▾
Show answer
BlockingQueue handles waiting and signalling for you. BlockingQueue<Integer> q = new ArrayBlockingQueue<>(10); Runnable producer = () -> { for (int i = 0; i < 100; i++) { try { q.put(i); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } } }; Runnable consumer = () -> { try { while (true) System.out.println(q.take()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }; put blocks when full and take blocks when empty.
40

What does the volatile keyword guarantee?

Save to ▾
Show answer
volatile guarantees visibility and ordering: a write to a volatile field is immediately visible to other threads, and it prevents reordering around that access under the Java Memory Model. It does not make compound operations like count++ atomic, because that is a read, modify and write. Use it for flags such as a stop signal, and use AtomicInteger or locks for counters.

Frequently asked questions

What does a Spring Boot Developer do?

A Spring Boot developer specialises in building microservices with the Spring ecosystem: Spring MVC, Spring Data, Spring Security and Spring Cloud. Daily work includes configuring services, securing APIs with JWT or OAuth2, adding observability with Actuator and Micrometer, and shipping through Docker and CI/CD pipelines. Companies moving from monoliths to microservices hire heavily for this role. Strong debugging habits and clear documentation are valued in this role.

Which skills are tested in a Spring Boot Developer interview?

Expect questions on Spring Boot, Spring Security, Microservices, Docker, Kafka. Most rounds also ask you to explain a project and solve one practical problem live.

How do I prepare for a Spring Boot Developer interview?

Revise Spring Boot and Microservices, JDBC, Hibernate and JPA, Multithreading and Concurrency first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.