Spring Boot Interview Questions & Answers (2025) — From Basics to Microservices

Rishabh Jain

Aug 27, 2025

5

mins

Spring Boot Interview Questions & Answers

What is Spring Boot?

Spring Boot is an opinionated framework that simplifies building production-ready Spring applications via auto-configuration, embedded servers, and starter dependencies.

Spring Boot reduces boilerplate so you can get a runnable app fast: it auto-configures sensible defaults based on what’s on the classpath, bundles an embedded servlet container (Tomcat/Jetty) so apps run as standalone JARs, and exposes “starter” packages that pull in common dependencies with one line. It’s built for production — config profiles, health checks, metrics, and a rich ecosystem (Spring Data, Security, Cloud) make mainstream backend work much faster.

“Recruiters love candidates who can explain what Spring Boot does in one sentence and then show a tiny code example — it proves both concept knowledge and hands-on ability.”

Spring Boot Interview Questions & Answers

Answer format & how to read this guide

Every question in this guide follows the same predictable, scannable structure so humans — and AI — can grab the answer fast:

1-line answer → short explanation (2–4 lines) → code sample → when to use → common pitfalls

That first single-sentence answer appears at the very top of every Q block so you can read the direct answer at a glance (or have a voice assistant read it), then expand for the details and runnable examples.

Why this format helps you

  • Fast scanning: Recruiters and engineers can verify a candidate’s knowledge in one line.

  • Clear teaching path: Short context, then a concrete example, then practical caveats.

  • Copyable code: You’ll get runnable snippets immediately, and “Try this in IDE” links so you can experiment.

Live example (how each Q will look)

What is @SpringBootApplication?

@SpringBootApplication is a meta-annotation that bundles @Configuration, @EnableAutoConfiguration, and @ComponentScan.

It marks the main class and tells Spring Boot to auto-configure beans and scan components from the package. Use it on your application entry point to bootstrap a runnable JAR.

Code sample (expandable, copyable):

@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

Try this in IDE: Run the demo project in the embedded playground (click Run in IDE) to see the app start and actuator /health appear.
When to use: Always on the main application class for typical Spring Boot apps.
Common pitfalls: Avoid placing this annotation in a sub-package that prevents component scanning of other packages.

“Try this in IDE” collapsible playgrounds

  • Each runnable code sample will include a collapsible playground with:

    • A one-click “Run in IDE” or “Open in Codespace/Gitpod” button.

    • Link to a minimal starter repo (one module) or a prebuilt sandbox.

    • Short run instructions (e.g., ./mvnw spring-boot:run or click ▶).

  • Playground content should be lightweight (single example) so experiments are instant and repeatable.

Expandable code blocks with copy button (UX & accessibility)

  • Visible UI: Every code block must be expandable/collapsible, show syntax highlighting, and include a one-tap Copy button.

  • Progressive enhancement: If JavaScript is disabled, the full code should still be visible and selectable (no hidden content).

  • Accessibility: Provide aria-label on copy buttons (e.g., aria-label="Copy @SpringBootApplication example").

  • Mobile: Keep horizontal scrolling minimal; wrap lines sensibly or offer a “view raw” link.

Implementation checklist for authors/devs (quick)

  • Put the one-line answer in the first paragraph of the H3.

  • Add the 2–4 sentence explanation immediately after.

  • Include an expandable code block with a copy button and a “Try this in IDE” link.

  • End the block with two short bullets: When to use and Common pitfalls.

  • Ensure the code is crawlable (plain text fallback) and the playground links are permanent.

Core / Basic Spring Boot Questions

Each question below starts with the one-line answer (first line) so it’s ready for featured snippets and voice responses — expand for a quick explanation and a tiny code example when helpful.

What is @SpringBootApplication?

@SpringBootApplication is a meta-annotation that bundles @Configuration, @EnableAutoConfiguration, and @ComponentScan.

Put this annotation on your main class to mark the app entry point — it enables auto-configuration and component scanning from that package downward so beans and configuration are discovered automatically. It’s the standard bootstrap annotation for most Spring Boot apps.

Code (main class):

@SpringBootApplication
public class DemoApplication {
  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

When to use: On the primary application class in nearly all Spring Boot projects.
Common pitfall: Placing it in a deep sub-package can limit component scanning; put it at a top-level package.

How does auto-configuration work?

Auto-configuration detects classes and dependencies on the classpath and conditionally registers beans using @Conditional rules to provide sensible defaults.

Spring Boot ships many @Configuration classes (via spring-boot-autoconfigure) that are evaluated at startup. Each config class uses conditional annotations like @ConditionalOnClass, @ConditionalOnMissingBean, or @ConditionalOnProperty so beans are only created when appropriate. You can disable or override auto-configs if you need custom behavior.

Code (example: exclude an auto-config):

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
public class DemoApplication { ... }

When to use: Rely on auto-config for quick start; exclude or provide your own beans when you need custom wiring.
Common pitfall: Relying on auto-config without understanding it can hide missing dependencies or unexpected beans.

Spring Boot starters — what are they?

Starters are opinionated dependency bundles (e.g., spring-boot-starter-web) that pull in a useful set of libraries so you don’t manage each transitive dependency manually.

Instead of adding Jackson, Spring MVC, and an embedded servlet dependency individually, include spring-boot-starter-web and you get a curated set that works together. Starters simplify dependency management and help you follow common conventions.

Pom example (Maven):

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>

When to use: Add the starter for the layer you need (web, data-jpa, security, test).
Common pitfall: Using too many starters can bloat the classpath; prefer targeted starters for production builds.

Embedded server vs external server — when to choose which?

Use an embedded server (runnable JAR) for faster dev, simpler deployment, and microservices; use an external server (WAR) when your organization requires centralized servlet containers or legacy deployment.

By default Spring Boot packages with an embedded Tomcat/Jetty/Undertow so you can run java -jar app.jar. For legacy environments that deploy to a shared application server, build a WAR and extend SpringBootServletInitializer. Embedded servers are the common choice for cloud and containerized deployments.

Code (WAR bootstrapping quick hint):

@SpringBootApplication
public class WarApplication extends SpringBootServletInitializer {
  @Override
  protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
    return application.sources(WarApplication.class);
  }
}

When to use: Embedded for microservices/containers; external WAR for legacy app-server policies.
Common pitfall: Forgetting to change packaging to war and to disable the embedded server when deploying to an external servlet container.

Core question

One-line answer

Tags

What is @SpringBootApplication?

Meta-annotation combining @Configuration, @EnableAutoConfiguration, @ComponentScan.

annotation, bootstrap

How does auto-configuration work?

Conditional configuration classes create beans based on classpath and properties.

auto-config, ConditionalOnClass, ConditionalOnProperty

What are Spring Boot starters?

Opinionated dependency bundles that bring in commonly required libraries.

starter, dependency-management

Embedded server vs external server?

Embedded JAR for microservices/cloud; WAR/external servlet for legacy app servers.

deployment, packaging, Tomcat


Classpath → Auto-config classes → Conditional checks → Beans created)

Web & REST (controllers, validation, error handling)

Each question below begins with a one-line lead. Expand for a short explanation, code, common pitfalls, and a “Try this in IDE” playground hint.

@RestController vs @Controller

@RestController returns response bodies (JSON/XML); @Controller returns views (templates) or uses @ResponseBody for API responses.

Table (quick compare)

Aspect

@RestController

@Controller

Primary Use

REST APIs — returns body (JSON/XML)

Server-side rendered views (Thymeleaf/JSP) or mixed

Implicit Behavior

Combines @Controller + @ResponseBody

Requires @ResponseBody on methods to return raw bodies

Typical return types

POJOs, ResponseEntity<>

String view names, ModelAndView

When to choose

Microservices, API backends

Traditional MVC apps, pages + forms

Example

@RestController with @GetMapping("/api")

@Controller with @GetMapping("/") returning "index"

When to use: Prefer @RestController for JSON REST services and @Controller when rendering HTML or templates.
Common pitfall: Mixing view logic and API endpoints in same controller—keep concerns separated.

Validation with @Valid / @Validated

Use @Valid on request bodies and @Validated on controller classes or method parameters to trigger JSR-380 (Bean Validation) checks (e.g., @NotNull, @Size).

Annotate your DTO fields (@NotNull, @Email, @Size) and place @Valid on the controller parameter (or @Validated at class level). Binding and validation errors are captured in BindingResult or thrown as MethodArgumentNotValidException which you should handle centrally.

Common usage patterns

  • public ResponseEntity<?> create(@Valid @RequestBody UserDto dto)

  • Use @Validated when you need validation groups or method-level validation for Spring components.

Common pitfalls

  • Forgetting @Valid on the controller parameter — validation won’t run.

  • Returning raw validation errors to clients without normalizing them.

Exception handling with @ControllerAdvice

Use @ControllerAdvice + @ExceptionHandler to centralize API error mapping and return consistent error responses.

Instead of try/catch in controllers, create a global handler that maps exceptions (e.g., MethodArgumentNotValidException, EntityNotFoundException) to ResponseEntity<ErrorDto> with proper HTTP status codes and structured error bodies.

Common patterns

  • Handle validation exceptions and return a field-level error list.

  • Map domain exceptions to 4xx and unexpected exceptions to 5xx with logging and correlation IDs.

Common pitfall: Returning raw stack traces or internal exceptions to clients — always sanitize.

Code block — sample controller + validation + service + global exception handler

Below is a compact, runnable example you can paste into a minimal Spring Boot app. It includes a DTO with validation, a @RestController endpoint, a simple service, and a @ControllerAdvice to normalize errors.

// UserDto.java
package com.example.demo.dto;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record UserDto(
  @NotBlank(message = "name is required") String name,
  @Email(message = "invalid email") String email,
  @Size(min = 6, message = "password min 6 chars") String password
) {}

// UserService.java
package com.example.demo.service;
import com.example.demo.dto.UserDto;
import org.springframework.stereotype.Service;

@Service
public class UserService {
  public String createUser(UserDto dto) {
    // stub: persist user and return id
    return "user-id-123";
  }
}

// UserController.java
package com.example.demo.controller;
import com.example.demo.dto.UserDto;
import com.example.demo.service.UserService;
import jakarta.validation.Valid;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/users")
public class UserController {
  private final UserService service;
  public UserController(UserService service) { this.service = service; }

  @PostMapping
  public ResponseEntity<?> createUser(@Valid @RequestBody UserDto dto) {
    String id = service.createUser(dto);
    return ResponseEntity.status(201).body(Map.of("id", id));
  }
}

// GlobalExceptionHandler.java
package com.example.demo.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

import java.util.stream.Collectors;

@ControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(MethodArgumentNotValidException.class)
  public ResponseEntity<?> handleValidation(MethodArgumentNotValidException ex) {
    var errors = ex.getBindingResult().getFieldErrors()
      .stream()
      .map(fe -> Map.of("field", fe.getField(), "message", fe.getDefaultMessage()))
      .collect(Collectors.toList());
    return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(Map.of("errors", errors));
  }

  @ExceptionHandler(Exception.class)
  public ResponseEntity<?> handleAll(Exception ex) {
    // log correlation id and message in real apps
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                         .body(Map.of("error", "Internal server error"));
  }
}

Try this in IDE (playground):

  • Create a new Spring Boot project with Spring Web and Validation (spring-boot-starter-validation or Jakarta validation dependency).

  • Place the files in com.example.demo package and run with ./mvnw spring-boot:run (or via your IDE).

  • POST to http://localhost:8080/api/users with invalid JSON to see validation response:

{ "name": "", "email": "nope", "password": "123" }

UI notes for authors/devs: Render the code block as expandable with a Copy button and a “Run in IDE” link to a minimal GitHub repo or Codespaces/Gitpod sandbox.

(Request → Controller → Service → Repo)

Quick checklist (do before an interview):

  • Validate DTOs with @Valid and test invalid payload responses.

  • Centralize API errors with @ControllerAdvice.

  • Keep controllers thin — move logic to services.

  • Return ResponseEntity<> with appropriate status codes (201 for create, 400 for validation, 404 for not found).

Data access & JPA

Clear, snippet-first answers, short explanations, and copy-ready code so you can speak confidently about Spring Data JPA in interviews.

Spring Data JPA basics (one-line lead)

Spring Data JPA provides repository abstractions (e.g., JpaRepository) that eliminate boilerplate CRUD and let you define query methods or JPQL/SQL queries.

Use JpaRepository / CrudRepository to get ready-made CRUD, paging and sorting. Define methods by naming convention (e.g., findByEmail) or with @Query for custom JPQL/SQL. Repositories work on Entities annotated with @Entity and typically return domain objects, Optional<T>, Page<T>, or projection interfaces/DTOs.

Common interview points: method-name queries, derived count/exists methods, Pageable/Sort, projections, and when to use native queries vs JPQL.
Pitfall: Don’t put business logic in repositories — keep them thin and testable.

Transactions — @Transactional propagation table (REQUIRED, REQUIRES_NEW, etc.)

Transaction propagation controls whether a method runs inside an existing transaction or starts a new one — pick the propagation that matches the business intent.

Propagation table (quick reference)

Propagation

One-line Meaning

When to use / note

REQUIRED

Join current tx or create one if none exists.

Default; typical for service methods.

REQUIRES_NEW

Suspend current tx and start a new independent tx.

Use for auditing or when you must commit regardless of outer tx.

SUPPORTS

Run within current tx if present; otherwise run non-transactional.

For read-only helpers reusable in/out of tx.

NOT_SUPPORTED

Suspend current tx — execute non-transactionally.

For read-only calls that must not participate.

MANDATORY

Must run inside an existing tx; throws exception otherwise.

When caller is responsible for creating tx.

NEVER

Must not run inside a transaction; throws if a tx exists.

Rare; enforce non-transactional behavior.

NESTED

Run in a nested savepoint (requires JDBC savepoints).

Useful for partial rollbacks within same connection (DB/driver must support).

Common pitfalls:

  • Putting @Transactional on private methods or on classes with proxying limitations — Spring AOP proxies only public (or at least non-private) calls via proxy.

  • Expecting REQUIRES_NEW to roll back outer tx — they’re independent.

  • Using NESTED assuming distributed transactions — nested uses savepoints on the same connection only.

N+1 problem & solutions (fetch join, EntityGraph)

The N+1 problem occurs when loading a collection lazily triggers one query for the parent and N additional queries for each child — solve it using fetch joins, @EntityGraph, batch fetching, or DTO projections.

Explanation & solutions:

  • Fetch join (JPQL): select p from Post p join fetch p.comments where p.id = :id loads parent and children in one query. Best when you know you need the relationship.

  • @EntityGraph: Declaratively hint to JPA to fetch certain associations without writing custom JPQL. Useful for repository methods.

  • Batch fetching / Hibernate @BatchSize: Reduces roundtrips by fetching collections in batches. Good for many-to-many or large collections.

  • DTO projection: Use select new com.example.dto.PostDto(p.id, p.title, c.content) to return flattened data avoiding entities and lazy problems.

  • Avoid iterating over collections in a loop that triggers lazy loads in templates or service logic.

Common pitfall: Using join fetch carelessly with pagination (it can produce duplicate root rows); prefer DTOs or two-query strategies for paged results.

Code: repository method + @Transactional example

Repository examples

// Post.java (excerpt)
@Entity
public class Post {
  @Id @GeneratedValue
  private Long id;

  private String title;

  @OneToMany(mappedBy = "post", fetch = FetchType.LAZY)
  private List<Comment> comments = new ArrayList<>();
  // getters/setters...
}

// PostRepository.java
public interface PostRepository extends JpaRepository<Post, Long> {

  // 1) Fetch join JPQL to load post + comments in one query
  @Query("select p from Post p left join fetch p.comments where p.id = :id")
  Optional<Post> findByIdWithComments(@Param("id") Long id);

  // 2) EntityGraph alternative (works nicely with Spring Data)
  @EntityGraph(attributePaths = {"comments"})
  Optional<Post> findWithCommentsById(Long id);

  // 3) DTO projection example
  @Query("select new com.example.dto.PostSummary(p.id, p.title, count(c)) " +
         "from Post p left join p.comments c where p.id = :id group by p.id, p.title")
  Optional<PostSummary> findSummaryById(@Param("id") Long id);
}

Service / @Transactional examples

@Service
public class PostService {

  private final PostRepository repo;
  private final AuditService auditService; // demonstrates REQUIRES_NEW

  public PostService(PostRepository repo, AuditService auditService) {
    this.repo = repo;
    this.auditService = auditService;
  }

  // Default propagation (REQUIRED) — joins or creates a transaction
  @Transactional
  public Long createPostAndComments(Post post, List<Comment> comments) {
    Post saved = repo.save(post);
    comments.forEach(c -> {
      c.setPost(saved)

@Service
public class AuditService {
  // REQUIRES_NEW: starts a separate transaction, commits/rolls back independently
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  public void recordCreation(Long postId) {
    // write audit record — this will commit even if the caller rolls back
  }
}

When to use & pitfalls demonstrated:

  • Use findByIdWithComments or @EntityGraph when you know you need the collection to avoid the N+1.

  • Use DTO projection when you only need a few fields and want to avoid fully initializing entities.

  • Beware join fetch with paging — it changes result cardinality; use projections or a two-step query (ids then fetch associations).

Quick checklist (prep for interviews)

  • Know when to use JpaRepository methods vs custom @Query.

  • Explain the N+1 problem and at least two mitigation techniques (fetch join, @EntityGraph, DTO projection).

  • Be ready to explain @Transactional propagation modes and an example where REQUIRES_NEW is appropriate.

  • Mention pitfalls: private transactional methods, join fetch + pagination, and lazy-loading in templates.

“Entity lifecycle” or “Bean lifecycle” (place near this section)

Security (modern Spring Security patterns)

Short, snippet-first answers, practical guidance, and copy-paste code you can run. Security questions often trip candidates up — show you know patterns and tradeoffs.

How to secure a Spring Boot REST API — 3 steps

Secure a Spring Boot REST API by (1) configuring stateless auth & authorization, (2) validating tokens/credentials at the edge, and (3) centralizing error handling + secure defaults (CORS, CSRF, headers).

Short explanation:

  1. Stateless auth & authorization: Use JWTs or OAuth2 Resource Server for APIs and set SessionCreationPolicy.STATELESS.

  2. Token validation at the gateway/edge: Verify signature, expiration, and scopes/roles before creating a SecurityContext.

  3. Secure defaults: Disable CSRF for pure JSON APIs, enable strict CORS rules, use secure headers, and expose minimal actuator endpoints in prod.

Common pitfalls: Leaving CSRF enabled when using cookie auth incorrectly, storing access tokens insecurely in client (localStorage), and not validating token audience/issuer.

JWT vs OAuth2 vs session auth (comparison table)

Approach

What it is

Pros

Cons

When to use

JWT (self-contained tokens)

Signed JSON Web Token carrying claims; validated by resource server.

Simple, scalable, no server session state, easy for microservices.

Token revocation is non-trivial; secret/key management; token size.

Microservices, mobile apps, stateless APIs.

OAuth2 (Authorization Server + Resource Server)

Standards-based auth with access + refresh tokens, scopes, flows (Auth code, client creds).

Best for delegated auth, third-party apps, refresh tokens, fine-grained scopes.

More moving parts (AS/RS); higher complexity to implement/maintain.

Public APIs, third-party integration, enterprise auth (SSO).

Session (server state)

Traditional cookie-based session stored server-side (or DB).

Simple logout/revocation, built-in CSRF protection with cookies.

Not ideal for horizontally scaled microservices unless shared session store.

Monoliths, server-rendered sites, apps needing strong session revocation.

Security checklist (quick): validate iss/aud, check expiry, use TLS everywhere, rotate keys, minimize token lifetime, use refresh tokens per OAuth2 patterns.

Code: lightweight JWT filter + modern Security DSL example

Below is a compact, modern Spring Security configuration (no WebSecurityConfigurerAdapter) with a JwtAuthFilter (extends OncePerRequestFilter) and a SecurityConfig registering a SecurityFilterChain. It uses io.jsonwebtoken.Jwts for token parsing — replace the secretKey with your key management (KMS, Vault).

Note: For production, validate issuer, audience, use asymmetric keys (RS256) and rotate keys via JWKS or a KMS.

// build.gradle (snippet) -> add io.jsonwebtoken
// implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
// runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5', 'io.jsonwebtoken:jjwt-jackson:0.11.5'

package com.example.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.stream.Collectors;

public class JwtAuthFilter extends OncePerRequestFilter {

  private final String secretKey; // ideally injected, use proper key management

  public JwtAuthFilter(String secretKey) {
    this.secretKey = secretKey;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request,
                                  HttpServletResponse response,
                                  FilterChain filterChain) throws jakarta.servlet.ServletException, java.io.IOException {

    String header = request.getHeader(HttpHeaders.AUTHORIZATION);
    if (header == null || !header.startsWith("Bearer ")) {
      filterChain.doFilter(request, response);
      return;
    }

    String token = header.substring(7);
    try {
      Claims claims = Jwts.parserBuilder()
          .setSigningKey(secretKey.getBytes(StandardCharsets.UTF_8))
          .build()
          .parseClaimsJws(token)
          .getBody();

      String username = claims.getSubject();
      var roles = claims.get("roles", List.class);
      var authorities = roles == null ? List.of() :
          roles.stream().map(r -> new SimpleGrantedAuthority((String) r)).collect(Collectors.toList());

      var auth = new UsernamePasswordAuthenticationToken(username, null, authorities);
      SecurityContextHolder.getContext().setAuthentication(auth);
    } catch (Exception ex) {
      // invalid token — clear context and continue (or send 401)
      SecurityContextHolder.clearContext();
    }

    filterChain.doFilter(request, response);
  }
}
// SecurityConfig.java
package com.example.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
public class SecurityConfig {

  private final String jwtSecret = "replace-with-secure-secret-managed-in-vault";

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http
      .csrf(csrf -> csrf.disable()) // JSON APIs typically disable CSRF; see note below
      .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
      .authorizeHttpRequests(auth -> auth
          .requestMatchers(HttpMethod.POST, "/api/auth/**").permitAll()
          .requestMatchers("/actuator/health").permitAll()
          .anyRequest().authenticated()
      )
      .addFilterBefore(new JwtAuthFilter(jwtSecret), UsernamePasswordAuthenticationFilter.class)
      .httpBasic(Customizer.withDefaults()); // optional: keep for basic auth on non-API endpoints

    return http.build();
  }
}

Key notes & best practices:

  • CSRF: Disable only for stateless JSON APIs. If you use cookie-based auth, enable CSRF and use SameSite cookies.

  • Key management: Don’t hardcode secrets. Use Vault, KMS, or JWKS endpoints with asymmetric keys (RS256).

  • Error handling: Return 401 for invalid/expired tokens; include clear error messages and correlation IDs for logs.

  • Scopes & roles: Use claims for roles/scopes and map them to GrantedAuthority carefully (avoid client-injected role escalation).

“Show both theory and practice: explain token flows (access vs refresh), then walk through a tiny JWT validation snippet — that proves you can both design and implement secure APIs.”

Interview talking points & follow-ups

  • Be ready to explain refresh token flows, refresh token rotation, and refresh token revocation strategies.

  • Discuss asymmetric keys & JWKS (how to configure a Resource Server to validate tokens with public keys).

  • Mention rate limiting and brute-force protections for auth endpoints.

  • If asked about SSO, explain how OAuth2 Authorization Code flow works and when to use it.

Want a runnable sample repo? I can prepare a minimal Spring Boot project (Maven/Gradle) with the JwtAuthFilter, login endpoint that issues JWTs (using JJWT), and integration tests demonstrating 401 vs 200 flows.

Microservices & Spring Cloud

Spring Boot vs Spring Cloud — one-line lead

Spring Boot builds individual services quickly; Spring Cloud provides the distributed-systems building blocks (discovery, config, circuit breakers, gateway) you need to run many services reliably.

Use Spring Boot to create runnable microservices (auto-config, starters, actuator). When you need service discovery, centralized config, routing, resiliency patterns, and distributed tracing across multiple Spring Boot services, add Spring Cloud components (Eureka/Consul/DiscoveryClient, Spring Cloud Config, Spring Cloud Gateway, Resilience4j). Interviewers want both implementation know-how and why you’d choose each component.

Service discovery (one-line lead)

Service discovery lets services find each other dynamically — use a registry (Eureka/Consul) or DNS-based discovery for cloud-native environments.

Register services with a discovery server; clients resolve service instances via the registry (client-side load balancing) or via server-side routing. Alternatives: Kubernetes DNS + headless services, or cloud provider service discovery (AWS Cloud Map). Understand pros/cons: registries add operational overhead but provide dynamic routing and health-aware instance lists.

Config server (one-line lead)

A centralized config server (Spring Cloud Config) externalizes and versions configuration so services load environment-specific properties at runtime.

Store properties in Git (or Vault) and let services fetch config on startup (and refresh via /actuator/refresh or bus events). Use Vault or KMS for secrets. Important: avoid putting secrets in plaintext Git; use encryption or Vault integration.

Circuit breaker (one-line lead)

Circuit breakers (Resilience4j) prevent cascading failures by short-circuiting calls to unhealthy services and providing fallbacks.

Configure thresholds (failure rate, slow call rate), time windows, and fallback strategies. Combine circuit breakers with bulkheads, retries, and timeouts for resilient call patterns. Know when to return cached data vs degrade gracefully.

Code — sample WebClient call example vs RestTemplate snippet

RestTemplate (blocking)

// RestTemplate usage (blocking)
@Service
public class LegacyClientService {

  private final RestTemplate restTemplate;

  public LegacyClientService(RestTemplate restTemplate) {
    this.restTemplate = restTemplate;
  }

  public String getRemoteData(String id) {
    String url = "http://service-b/api/data/" + id;
    ResponseEntity<String> resp = restTemplate.getForEntity(url, String.class);
    return resp.getBody();
  }
}

WebClient (non-blocking, recommended for reactive stacks and high concurrency)

// WebClient usage (non-blocking)
@Service
public class ReactiveClientService {

  private final WebClient webClient;

  public ReactiveClientService(WebClient.Builder webClientBuilder) {
    this.webClient = webClientBuilder.baseUrl("http://service-b").build();
  }

  public Mono<String> getRemoteData(String id) {
    return webClient.get()
      .uri("/api/data/{id}", id)
      .retrieve()
      .onStatus(HttpStatus::is4xxClientError, resp -> Mono.error(new RuntimeException("4xx")))
      .onStatus(HttpStatus::is5xxServerError, resp -> Mono.error(new RuntimeException("5xx")))
      .bodyToMono(String.class);
  }
}

Usage notes:

  • Use RestTemplate in legacy blocking apps (it’s still fine for simple uses). Spring recommends WebClient for new, non-blocking services and for better connection/resource utilization.

  • In a non-reactive (MVC) app, you can still use WebClient and block() carefully for simple calls — prefer non-blocking when your stack supports it.

Table: RestTemplate vs WebClient (side-by-side pros/cons)

Feature

RestTemplate

WebClient

API type

Blocking (synchronous)

Non-blocking (reactive)

Typical use

Legacy / simple blocking calls

High concurrency, reactive apps, streaming

Resource usage

Each call uses thread; less efficient under load

Lower thread usage; better throughput

Streaming support

Limited

First-class (Flux)

Error handling

ResponseErrorHandler

onStatus() + reactive error signalling

Learning curve

Low

Higher (reactor + backpressure concepts)

When to prefer

Simple services, synchronous code paths

Microservices, high scale, async flows

Interview talking points & best practices

  • Service discovery: explain client-side (Eureka + Ribbon historically) vs server-side (API Gateway) and Kubernetes alternatives.

  • Config management: mention Spring Cloud Config and Vault for secrets.

  • Resilience: discuss circuit breakers (Resilience4j), retries with backoff, timeouts, and bulkheads.

  • Observability: trace calls with Sleuth/OpenTelemetry and propagate correlation IDs for logs/tracing.

  • Protocol considerations: use HTTP/2/gRPC for internal calls when low latency and streaming matter.

Quick checklist for interview answers

  • Define the role of Spring Cloud components (discovery, config, gateway, circuit breaker).

  • Show code: give a concise WebClient example and explain why it’s preferable at scale.

  • Describe tradeoffs: operational complexity of service registry vs benefits (health-aware routing).

  • Mention observability and fault-tolerance patterns (tracing + circuit breaker + fallback).

Production readiness & monitoring

Make your service production-ready by exposing safe Actuator endpoints, instrumenting app behavior with Micrometer, and shipping metrics to Prometheus + visualizing with Grafana.

Production readiness is about observability, safe defaults, and operational hygiene — not just "it runs." Expose only necessary actuator endpoints, secure them, emit meaningful metrics and traces, and use a metrics stack so SREs can alert on real issues before users do.

Actuator endpoints you should know (health, metrics) — one-line lead

Key actuator endpoints are /actuator/health, /actuator/metrics, /actuator/prometheus, /actuator/info — use them for health checks, telemetry, and deployment validation.

Short notes:

  • /actuator/health — basic readiness & liveness checks (customize checks for DB, caches).

  • /actuator/metrics — exposes counters, timers, gauges (Micrometer-backed).

  • /actuator/prometheus — if using Prometheus, enable this endpoint for scraping.

  • /actuator/info — surface build/version info (git commit, build time).
    Security: Never expose all actuator endpoints publicly — restrict via security rules, IP allowlists, or an API gateway.

Custom health indicators & Micrometer examples — one-line lead

Add HealthIndicator implementations for domain checks and use Micrometer Counter/Timer to emit business & performance metrics.

Custom HealthIndicator (example):

@Component
public class DatabaseHealthIndicator implements HealthIndicator {
  private final DataSource ds;
  public DatabaseHealthIndicator(DataSource ds) { this.ds = ds; }

  @Override
  public Health health() {
    try (var conn = ds.getConnection()) {
      if (conn.isValid(1000)) return Health.up().withDetail("db", "reachable").build();
      return Health.down().withDetail("db", "unreachable").build();
    } catch (Exception ex) {
      return Health.down(ex).withDetail("error", ex.getMessage()).build();
    }
  }
}

Micrometer counters & timers (example):

@Service
public class OrderService {
  private final Counter ordersCreated;
  private final Timer processTimer;

  public OrderService(MeterRegistry registry) {
    this.ordersCreated = Counter.builder("orders.created").description("New orders").register(registry);
    this.processTimer = Timer.builder("orders.process.time").publishPercentiles(0.5, 0.95).register(registry);
  }

  public void process(Order order) {
    processTimer.record(() -> {
      // processing logic
      ordersCreated.increment();
    });
  }
}

Prometheus scraping: enable management.endpoint.prometheus and permit /actuator/prometheus for the Prometheus server to scrape.

Checklist: production logging, profiles, secrets management (Vault)

Production logging

  • Use structured JSON logs (e.g., Logback JSON encoder) for easier parsing.

  • Include correlation IDs (request id) in logs and propagate them across threads/services.

  • Set appropriate log levels (INFO for prod, debug toggled via actuator/env on staging only).

Profiles & configuration

  • Use Spring profiles (application-prod.yml) to separate prod settings.

  • Avoid embedding prod secrets in property files — use Vault/KMS.

  • Make sensitive config reloadable where safe (use config server + refresh or Vault dynamic secrets).

Secrets management (Vault best practices)

  • Do not hardcode secrets; inject at runtime from Vault/KMS.

  • Use short-lived credentials where possible (DB creds via Vault dynamic secrets).

  • Ensure apps authenticate to Vault via an instance identity (IAM, Kubernetes service account) rather than static tokens.

diagram showing Micrometer in-app instrumentation → Prometheus scraping → Grafana dashboards. Accent color used for headings

Quick operational tips

  • Alert on symptoms, not just metrics: error-rate, p50/p95 latency, DB connection pool saturation.

  • Test health endpoints: let your orchestrator (K8s / ELB) use /actuator/health/readiness vs liveness.

  • Avoid noisy metrics: sample wisely and publish percentiles for latency (p50/p95/p99).

  • Automate runbook links in alerts: include playbook URL and correlation-id for faster MTTR.

Testing Spring Boot applications

Unit vs integration tests (one-line lead)

Unit tests verify small, isolated units (fast, no Spring context); integration tests load Spring slices or the full context to validate wiring, persistence, and web layers.

Short explanation:

  • Unit tests: Plain JUnit + Mockito for service logic, utilities—very fast (<50ms).

  • Integration tests (slices): @WebMvcTest, @DataJpaTest load focused parts of the framework with auto-configured helpers (MockMvc, TestEntityManager).

  • Full integration: @SpringBootTest boots the whole app (useful for end-to-end flows, but slower).
    Pitfall: Relying only on full-context tests makes feedback slow; favor a mix—lots of unit tests, targeted slice tests, a few end-to-end tests.

@WebMvcTest, @DataJpaTest, @SpringBootTest usage examples (one-line lead each)

@WebMvcTest (one-line): Loads only web layer (controllers, Jackson, validation) and auto-configures MockMvc for controller tests.

  • When: Test controllers, request validation, and serialization.

  • Pitfall: Repositories/services must be @MockBeaned; it does NOT load full Spring context.

@DataJpaTest (one-line): Loads JPA components and configures an in-memory DB (H2) for repository testing.

  • When: Test repository queries, mappings, and @Entity behavior.

  • Tip: Use @AutoConfigureTestDatabase(replace = NONE) + Testcontainers for real DB testing.

@SpringBootTest (one-line): Boots the full application context; use with webEnvironment to run on a random port for end-to-end tests.

  • When: Full integration tests, health checks, actuator integration, or when multiple layers must be validated together.

  • Pitfall: Slow—use sparingly.

Code: MockMvc example (expandable block + “Try this in IDE”)

Use @WebMvcTest + MockMvc to assert controller behavior and validation without starting the full app.

Controller under test (example)

// src/main/java/com/example/demo/controller/UserController.java
@RestController
@RequestMapping("/api/users")
public class UserController {
  private final UserService service;
  public UserController(UserService service) { this.service = service; }

  @PostMapping
  public ResponseEntity<Map<String,String>> create(@Valid @RequestBody UserDto dto) {
    String id = service.createUser(dto);
    return ResponseEntity.status(HttpStatus.CREATED).body(Map.of("id", id));
  }
}

Test using MockMvc

// src/test/java/com/example/demo/controller/UserControllerTest.java
package com.example.demo.controller;

import com.example.demo.dto.UserDto;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;

import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@WebMvcTest(UserController.class)
class UserControllerTest {

  @Autowired MockMvc mockMvc;
  @Autowired ObjectMapper objectMapper;

  @MockBean UserService userService;

  @Test
  void createUser_returns201_andId() throws Exception {
    var dto = new UserDto("Alice","alice@example.com","s3cr3t");
    when(userService.createUser(dto)).thenReturn("id-123");

    mockMvc.perform(post("/api/users")
        .contentType(MediaType.APPLICATION_JSON)
        .content(objectMapper.writeValueAsString(dto)))
      .andExpect(status().isCreated())
      .andExpect(jsonPath("$.id").value("id-123"));
  }

  @Test
  void createUser_validationError_returns400() throws Exception {
    var dto = new UserDto("","bad-email","123"); // invalid
    mockMvc.perform(post("/api/users")
        .contentType(MediaType.APPLICATION_JSON)
        .content(objectMapper.writeValueAsString(dto)))
      .andExpect(status().isBadRequest())
      .andExpect(jsonPath("$.errors").exists());
  }
}

Try this in IDE (playground):

  • Create a minimal Spring Boot project with Spring Web, Spring Boot Starter Validation, and spring-boot-starter-test.

  • Add the controller, DTO with @NotBlank/@Email/@Size constraints, the service interface, and the test above.

  • Run ./mvnw test or use your IDE Test runner to execute UserControllerTest.

UX notes for article: render this test block as expandable with Copy and a “Run tests” / “Run in IDE” link to the starter repo.

Test pyramid showing many unit tests, fewer integration tests, and few end-to-end tests.

Quick checklist for interviews & real projects

  • Write fast unit tests for business logic (no Spring context).

  • Use @WebMvcTest for controller behavior and validation (MockMvc).

  • Use @DataJpaTest for repository testing; consider Testcontainers for real DB tests.

  • Use a few @SpringBootTest(webEnvironment = RANDOM_PORT) tests for end-to-end behavior.

  • Prefer deterministic tests: mock external calls, use embedded DB or Testcontainers, reset state between tests.

Deployment & CI/CD

Package, containerize, and automate — pick the packaging model (JAR/WAR), build a small Docker image, and wire a simple CI pipeline to run tests, build artifacts, and push images.

Packaging: JAR vs WAR (one-line lead)

Use an executable JAR (embedded server) for cloud/container-first apps; use a WAR for legacy app-server deployment policies.

Short explanation:

  • JAR (recommended): spring-boot-starter packages an app with an embedded servlet container (Tomcat/Jetty/Undertow) so you run java -jar app.jar. Easy for Docker and cloud.

  • WAR (legacy): Build a WAR to deploy to servlet containers (Tomcat, WebLogic). Requires SpringBootServletInitializer and different packaging. Use when orgs mandate central app servers.
    Pitfalls: WARs add operational constraints; JARs are ideal for microservices and containers.

Dockerfile example (expandable) + GitHub Actions CI snippet (copy button)

Create a minimal multi-stage Dockerfile for small images and a GitHub Actions workflow that runs tests, builds a JAR, builds the image, and pushes to a registry.

Dockerfile (multi-stage, Maven)

# ----- build stage -----
FROM eclipse-temurin:17-jdk as build
WORKDIR /workspace
COPY pom.xml mvnw ./
COPY .mvn .mvn
RUN ./mvnw -q -N dependency:go-offline

COPY src ./src
RUN ./mvnw -DskipTests package -DskipITs -q

# ----- runtime stage -----
FROM eclipse-temurin:17-jre
ARG JAR_FILE=target/*.jar
COPY --from=build /workspace/${JAR_FILE} app.jar
EXPOSE 8080
ENTRYPOINT ["java","-XX:+UseContainerSupport","-jar","/app.jar"]

GitHub Actions workflow (ci.yml)

name: CI

on: [push, pull_request]

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    env:
      REGISTRY: ghcr.io
      IMAGE_NAME: ${{ github.repository }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: 17

      - name: Cache Maven local repo
        uses: actions/cache@v4
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
          restore-keys: |
            ${{ runner.os }}-m2-

      - name: Build & test
        run: ./mvnw -B clean verify

      - name: Build Docker image
        run: |
          docker build -t $REGISTRY/${IMAGE_NAME}:${{ github.sha }} .
      
      - name: Log in to registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ secrets.REGISTRY_USER }}
          password: ${{ secrets.REGISTRY_TOKEN }}

      - name: Push image
        run: |
          docker tag $REGISTRY/${IMAGE_NAME}:${{ github.sha }} $REGISTRY/${IMAGE_NAME}:latest
          docker push $REGISTRY/${IMAGE_NAME}:${{ github.sha }}
          docker push $REGISTRY/${IMAGE_NAME}:latest

Copy tips: add secrets REGISTRY_USER and REGISTRY_TOKEN in repo settings. For DockerHub/GCR/ECR adjust registry/login steps.

Deployment options (Heroku / ECS / EKS / Cloud Run) — pros/cons

Platform

Pros

Cons

When to pick

Heroku

Fast PaaS, simple git push, good dev DX

Less control, cost at scale

MVPs, small teams

ECS (Fargate)

AWS native, serverless containers, integrates with IAM

AWS config complexity

Teams on AWS wanting managed containers

EKS (Kubernetes)

Full control, powerful orchestration, huge ecosystem

Operational complexity, steep learning

Large-scale, microservices with custom needs

Cloud Run

Serverless containers, auto-scale to zero, simple

Cold starts, limited control over infra

Event-driven APIs, cost-sensitive scaling

“Always bake your health check and readiness probe into the container — orchestration without good health checks is guesswork.”

Advanced topics & system design (senior-level)

Senior interviews expect system-level thinking: resilience patterns, observability, scaling strategies, and tradeoffs for distributed systems.

Designing resilient microservices (one-line lead)

Design resilient microservices with timeouts, retries with backoff, circuit breakers, bulkheads, and graceful degradation.

Short explanation / bullets:

  • Timeouts: Set sensible request timeouts to avoid resource exhaustion.

  • Retries with jitter/backoff: Retry transient errors; avoid thundering herds.

  • Circuit breakers: Fail fast when a downstream is unhealthy and provide fallbacks.

  • Bulkheads: Partition resources (thread pools) per downstream to prevent cascading failures.

  • Graceful degradation: Return cached or reduced functionality when dependencies fail.

Design note: discuss tradeoffs (user experience vs complexity) and where to place these patterns: client-side, gateway, or server.

Observability: tracing, correlation IDs, OpenTelemetry

Implement traces + logs + metrics with correlation IDs and OpenTelemetry to trace requests across services.

Short explanation:

  • Correlation IDs: Generate/request-propagate a request id header (e.g., X-Request-ID) and log it across services.

  • Tracing: Use OpenTelemetry or Zipkin/Sleuth to capture spans and latencies across microservice calls.

  • Metrics: Emit business and system metrics via Micrometer and scrape them with Prometheus.

  • Why: Observability is essential to troubleshoot latency, errors, and to understand user flows.

Multi-tenant strategies & DB scaling (one-line lead)

Choose single-db multi-tenant, schema-per-tenant, or DB-per-tenant based on isolation needs, scale, and operational cost.

Short explanation / options:

  • Single DB, shared schema: Easiest to manage; use tenant_id column; lower isolation.

  • Schema per tenant: Better isolation, manageable with migration tooling; good middle-ground.

  • DB per tenant: Highest isolation; heavy operational cost; best for high compliance needs.
    DB scaling tips: use read replicas, partitioning, sharding, and caching (Redis) for performance; use connection pools tuned per tenant.

Coding tasks & whiteboard prompts (practical)

Provide clear acceptance criteria, edge cases, and a runnable starter so interviewers can evaluate correctness, design, and testing.

Small REST API implementation task (acceptance criteria)

Task: Implement a simple Task Manager REST API with endpoints to create, get, update, and list tasks.
Acceptance criteria:

  • POST /api/tasks → create task, return 201 with id. Validate title non-empty.

  • GET /api/tasks/{id} → return 200 with task or 404 if not found.

  • PUT /api/tasks/{id} → update title/description, return 200.

  • GET /api/tasks → support pagination (page, size) and optional status filter.

  • Use @Valid for input validation, ResponseEntity for proper statuses, and a @ControllerAdvice for errors.

  • Include 3 unit tests for service logic and 2 MockMvc tests for controller endpoints.
    Evaluation focus: API correctness, DTOs, validation, transaction handling, tests, and clear README with run instructions.

Debugging a stacktrace — guided exercise

Diagnose a failing call by reading the stacktrace, identifying the root cause (e.g., LazyInitializationException or NullPointerException), and proposing a fix and test.

Example stacktrace (short):

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.example.Post.comments, could not initialize proxy - no Session
  at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:611)
  at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:211)
  at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:138)
  ...
  at com.example.controller.PostController.getPost(PostController.java:45)

Debug steps:

  1. Read the exception: LazyInitializationException — collection accessed outside transactional session.

  2. Find code line: PostController.getPost accesses post.getComments().size() or serializes post after session closed.

  3. Fix options:

    • Use fetch join in repository (findByIdWithComments) or @EntityGraph.

    • Or initialize the collection inside a @Transactional service method.

    • Or map to a DTO in the service layer that selects needed fields.

  4. Test: Add an integration test that loads entity and asserts comments are present with the chosen fix.

  5. Explain tradeoffs: fetch join can break pagination; DTO avoids entity lifecycle issues.

Collapsible code sandbox links and starter GitHub repo references

  • Starter repo name suggestion: github.com/your-org/spring-boot-interview-starters

  • Included folders per task: task-manager/, jwt-auth/, webflux-client/, test-samples/

  • Run instructions (to include in README): ./mvnw spring-boot:run and ./mvnw test
    (When publishing, replace placeholders with real repo links or Gitpod/Codespaces launch URLs.)

Study plan & cheat sheet (30/60/90 days)

Structured study: start with fundamentals (30 days), add mid-level topics and testing (60 days), then practice system design, microservices, and mock interviews (90 days).

30 / 60 / 90 breakdown (daily topics)

30 days — Fundamentals (daily ~1 hour)

  • Week 1: Spring Boot basics, @SpringBootApplication, starters, running apps.

  • Week 2: Controllers, REST, validation, @RestController. Practice small APIs.

  • Week 3: JPA basics, repositories, transactions, pagination.

  • Week 4: Testing basics: unit tests + @WebMvcTest.

60 days — Intermediate (daily ~1–1.5 hours)

  • Week 5: Spring Security basics and JWT flows.

  • Week 6: Microservices basics, WebClient, basic Spring Cloud concepts.

  • Week 7: Observability: Actuator, Micrometer, Prometheus.

  • Week 8: Deployment: Docker, basic CI pipeline, and GitHub Actions.

90 days — Advanced (daily ~1–2 hours)

  • Week 9: Resilience patterns: circuit breaker, retries, bulkheads.

  • Week 10: System design basics — design a microservice architecture.

  • Week 11: Performance tuning & troubleshooting (N+1, connection pools).

  • Week 12: Mock interviews, whiteboard tasks, and final cheat-sheet review.

Downloadable cheat sheet: “50 most likely Spring Boot interview Qs” — include short one-line answers + 3 code snippets (controller, @Transactional, WebClient) and a one-page rubric. (I can generate the PDF on request.)

Common pitfalls & quick fixes (Do’s & Don’ts)

One-line lead: Prevent common mistakes by following a short Do/Don’t checklist and fixing common anti-patterns before interviews.

“Interviewers reward clarity — present the issue, the fix, and the test you’d write. Short and structured.”

Do’s

  • Do use constructor injection (avoid field injection).

  • Do validate inputs with @Valid and handle errors centrally.

  • Do write small, focused tests — unit first, slices second.

  • Do explain tradeoffs when choosing tools/patterns.

Don’ts

  • Don’t put heavy logic in controllers.

  • Don’t rely solely on @Transactional on private methods.

  • Don’t block reactive stacks (avoid blocking calls in WebFlux).

  • Don’t expose actuator endpoints publicly without protection.

1-page checklist: “Fix these before your interview”

  • Use constructor injection for beans.

  • DTOs for external API models; avoid exposing internal entities.

  • Add @Valid and test invalid payloads.

  • Add basic actuator health + prometheus endpoint (secured).

  • Add unit tests for key service methods.

  • Avoid LazyInitializationException by using DTOs or fetch strategies.

  • Dockerfile builds small, multi-stage image.

  • README explains how to run app + tests.

FAQs

  1. What is Spring Boot used for?
    Spring Boot simplifies building production-ready Spring applications with auto-configuration and embedded servers.

  2. How is Spring Boot different from Spring MVC?
    Spring MVC is a web framework for building controllers/views; Spring Boot is an opinionated runtime that packages and auto-configures Spring apps.

  3. What is auto-configuration?
    Auto-configuration conditionally creates beans based on classpath and properties to provide sensible defaults.

  4. What is @SpringBootApplication?
    A meta-annotation that bundles @Configuration, @EnableAutoConfiguration, and @ComponentScan.

  5. When should I use @Transactional?
    On service-layer methods that require atomic DB operations — not on private methods.

  6. What is the N+1 problem and how do I fix it?
    The N+1 problem is extra DB queries caused by lazy loads; fix with fetch joins, @EntityGraph, batching, or DTO projections.

  7. How do I validate request input in controllers?
    Use @Valid on @RequestBody DTOs and annotate fields with JSR-380 annotations like @NotBlank, @Email.

  8. How do I test controllers?
    Use @WebMvcTest + MockMvc for controller slice tests and @SpringBootTest for end-to-end tests.

  9. Should I use RestTemplate or WebClient?
    Use RestTemplate for simple blocking needs; WebClient for non-blocking, high-concurrency or streaming use cases.

  10. How do I secure a REST API?
    Use stateless auth (JWT or OAuth2 Resource Server), validate tokens at the edge, and use secure defaults for CORS/CSRF.

  11. What actuator endpoints should I expose?
    /actuator/health, /actuator/metrics, and /actuator/prometheus (secured) are commonly used.

  12. How do I handle exceptions globally?
    Use @ControllerAdvice with @ExceptionHandler to map exceptions to structured ResponseEntity payloads.

  13. What is Spring Data JPA?
    Abstractions over JPA that provide repository interfaces for CRUD, paging, sorting, and derived queries.

  14. How to prevent leaking DB credentials?
    Use Vault/KMS or environment-based secrets, not hardcoded config files.

  15. When to use DTOs vs Entities in controllers?
    Use DTOs for API boundaries to avoid exposing internal entity state and lazy-loading issues.

  16. How do I debug LazyInitializationException?
    Identify where lazy collection is accessed outside a transactional context and use fetch joins or DTO mapping to fix.

  17. How to instrument metrics?
    Use Micrometer to create counters and timers and export to Prometheus; visualize in Grafana.

  18. What is a circuit breaker?
    A resilience pattern that trips after failure thresholds to prevent cascading failures and allow recovery.

  19. How to run integration tests with a real DB?
    Use Testcontainers to spin up ephemeral DB instances during CI and @DataJpaTest with @AutoConfigureTestDatabase(replace=NONE).

  20. How do I deploy a Spring Boot app to containers?
    Build an executable JAR, create a small multi-stage Docker image, push to a registry, and deploy to your chosen platform (Cloud Run/ECS/EKS).

  21. What should I prepare for a senior Spring Boot interview?
    Expect system design, resiliency patterns, observability, operational readiness, and tradeoffs for architecture choices.

Conclusion

You made it — solid work. Spring Boot interviews reward clarity, practical examples, and the ability to explain tradeoffs. Nail the one-line answers, back them with a tiny code sample, and show an operational mindset (testing, security, observability, deployment). That combo wins interviews.

Quick takeaways

  • Lead with a one-line answer, then show a short example.

  • Practice common patterns: auto-config, controllers, JPA, security, WebClient.

  • Prepare 2–3 real mini-demos (Task API, JWT auth, a fetch-join example).

  • Know production concerns: Actuator, Micrometer, health checks, and CI/CD.

  • Use the 30/60/90 plan: fundamentals → intermediate → system design + mock interviews.

Turn

failed interviews

into

offers accepted

with Interview Sidekick

Get Started

Interview Prep

Prepare for job interviews with real questions asked at real companies.

Real-Time Interview Assistance

Activate your ultimate sidekick in your interview browser for real-time interview guidance.

Question Bank

Browse through 10,000+ interview questions so that you can know what to expect in your upcoming interview.

Turn

failed interviews

into

offers accepted

with Interview Sidekick

Get Started

Interview Prep

Prepare for job interviews with real questions asked at real companies.

Real-Time Interview Assistance

Activate your ultimate sidekick in your interview browser for real-time interview guidance.

Question Bank

Browse through 10,000+ interview questions so that you can know what to expect in your upcoming interview.

Turn

failed interviews

into offers accepted

with Interview Sidekick

Get Started

Interview Prep

Prepare for job interviews with

real questions asked at

real companies.

Real-Time Interview Assistance

Activate your ultimate sidekick in

your interview browser for

real-time interview guidance.

Question Bank

Browse through 10,000+ interview

questions so that you can know

what to expect in your

upcoming interview.