Toolmingo
Guides22 min read

50 Spring Boot Interview Questions (With Answers)

Top Spring Boot interview questions with clear answers and code examples — covering auto-configuration, dependency injection, REST APIs, JPA, security, testing, and microservices.

Spring Boot interviews test your understanding of the framework's auto-configuration magic, Spring ecosystem integration, REST API design, data access patterns, and production readiness. This guide covers the 50 most common questions — with concise answers and runnable code examples.

Quick reference

Topic Most asked questions
Core concepts Auto-configuration, starters, @SpringBootApplication
Dependency Injection @Component, @Autowired, @Bean, scopes
REST APIs @RestController, @RequestMapping, status codes
Data access Spring Data JPA, repositories, transactions
Security Spring Security, JWT, OAuth 2.0
Testing @SpringBootTest, MockMvc, @DataJpaTest
Actuator Health checks, metrics, custom endpoints
Microservices Feign, Circuit Breaker, Config Server

Core Concepts

1. What is Spring Boot and how does it differ from Spring Framework?

Spring Boot is an opinionated, convention-over-configuration layer on top of the Spring Framework that eliminates boilerplate setup. Key differences:

Aspect Spring Framework Spring Boot
Configuration Manual XML or Java config Auto-configuration via starters
Embedded server Needs external WAR deployment Embedded Tomcat/Jetty/Undertow
Dependency management Manual version management Curated BOMs via spring-boot-starter-*
Production readiness DIY Actuator out of the box
Bootstrap AnnotationConfigApplicationContext SpringApplication.run()

Spring Boot adds auto-configuration, starters, an embedded server, and production tooling. You still use all of Spring (IoC, AOP, MVC, Security, Data) — Boot just removes the wiring overhead.


2. What does @SpringBootApplication do?

@SpringBootApplication is a convenience annotation that combines three annotations:

@SpringBootConfiguration   // same as @Configuration — marks this as a config class
@EnableAutoConfiguration   // triggers Spring Boot's auto-config mechanism
@ComponentScan             // scans the current package and sub-packages for beans
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class, args);
    }
}

You can exclude specific auto-configurations:

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

3. How does Spring Boot auto-configuration work?

Auto-configuration is driven by spring.factories (Spring Boot 2) or AutoConfiguration.imports (Spring Boot 3):

  1. Spring Boot scans classpath for jars.
  2. Each jar can declare auto-configuration classes in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
  3. These classes use @ConditionalOn* annotations to activate only when conditions are met.
@AutoConfiguration
@ConditionalOnClass(DataSource.class)          // only if DataSource is on classpath
@ConditionalOnMissingBean(DataSource.class)    // only if user hasn't defined one
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
    @Bean
    public DataSource dataSource(DataSourceProperties props) {
        return props.initializeDataSourceBuilder().build();
    }
}

Key @Conditional* annotations:

Annotation Activates when...
@ConditionalOnClass Class is on classpath
@ConditionalOnMissingClass Class is NOT on classpath
@ConditionalOnBean Bean of type exists
@ConditionalOnMissingBean Bean of type does NOT exist
@ConditionalOnProperty Property has specific value
@ConditionalOnWebApplication Running as web app

4. What are Spring Boot starters?

Starters are curated dependency descriptors that pull in everything needed for a feature:

<!-- Adds Spring MVC + embedded Tomcat + Jackson -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Adds Spring Data JPA + Hibernate + connection pool -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Common starters:

Starter What it includes
spring-boot-starter-web Spring MVC, embedded Tomcat, Jackson
spring-boot-starter-data-jpa Spring Data JPA, Hibernate
spring-boot-starter-security Spring Security
spring-boot-starter-test JUnit 5, Mockito, AssertJ, MockMvc
spring-boot-starter-actuator Health, metrics, info endpoints
spring-boot-starter-validation Bean Validation (Hibernate Validator)
spring-boot-starter-cache Spring caching abstraction

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

All four are specializations of @Component and trigger classpath scanning:

Annotation Layer Extra behavior
@Component Generic None
@Service Business logic None (semantic only)
@Repository Data access Translates JPA/JDBC exceptions to Spring DataAccessException
@Controller Web MVC Maps to HTTP requests; combines with @ResponseBody@RestController

Dependency Injection

6. What are the types of dependency injection in Spring?

Spring supports three styles:

// 1. Constructor injection (PREFERRED — enables immutability, easier testing)
@Service
public class OrderService {
    private final PaymentService paymentService;

    public OrderService(PaymentService paymentService) {  // @Autowired optional since Spring 4.3
        this.paymentService = paymentService;
    }
}

// 2. Setter injection (use for optional dependencies)
@Service
public class ReportService {
    private EmailService emailService;

    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
}

// 3. Field injection (convenient but harder to test — avoid in production code)
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // not recommended
}

Prefer constructor injection: it makes dependencies explicit, supports immutability, and simplifies unit testing without a Spring context.


7. What is the difference between @Bean and @Component?

Aspect @Bean @Component
Where used Inside @Configuration class on a method On a class
What it creates Return value of the method Instance of the annotated class
Use case Third-party classes you can't annotate Your own classes
Control Full control over instantiation Spring creates via no-arg constructor
// @Bean — needed for third-party class
@Configuration
public class AppConfig {
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }
}

// @Component — your own class
@Component
public class EmailValidator {
    public boolean isValid(String email) { /* ... */ }
}

8. What are Spring bean scopes?

Scope Lifetime Use case
singleton (default) One instance per Spring context Stateless services
prototype New instance per injection/request Stateful objects
request One per HTTP request Request-scoped data
session One per HTTP session User session data
application One per ServletContext App-wide shared state
@Component
@Scope("prototype")
public class ShoppingCart { /* ... */ }

Gotcha: Injecting a prototype bean into a singleton bean gives you the same prototype instance forever. Fix with @Lookup or ObjectProvider<T>.


9. What is @Qualifier and when do you use it?

When multiple beans implement the same interface, Spring can't decide which to inject — @Qualifier disambiguates:

public interface NotificationService { void send(String message); }

@Service("emailNotification")
public class EmailNotificationService implements NotificationService { /* ... */ }

@Service("smsNotification")
public class SmsNotificationService implements NotificationService { /* ... */ }

@Service
public class AlertService {
    private final NotificationService notificationService;

    public AlertService(@Qualifier("smsNotification") NotificationService notificationService) {
        this.notificationService = notificationService;
    }
}

Alternatively use @Primary to mark the default candidate.


10. What is @Value and how does it work?

@Value injects values from application.properties, environment variables, or SpEL expressions:

@Component
public class StripeConfig {
    @Value("${stripe.api.key}")
    private String apiKey;

    @Value("${stripe.webhook.secret:default-secret}")  // with default
    private String webhookSecret;

    @Value("#{systemProperties['user.home']}")          // SpEL
    private String userHome;

    @Value("#{T(java.lang.Math).PI}")                   // SpEL math
    private double pi;
}

Prefer @ConfigurationProperties for groups of related properties — it gives type safety and IDE completion.


REST APIs

11. What is the difference between @Controller and @RestController?

@Controller  // returns view names (for Thymeleaf / JSP)
public class PageController {
    @GetMapping("/home")
    public String home(Model model) {
        model.addAttribute("message", "Hello");
        return "home";  // resolves to templates/home.html
    }
}

@RestController  // = @Controller + @ResponseBody on every method
public class ApiController {
    @GetMapping("/api/users")
    public List<User> getUsers() {
        return userService.findAll();  // serialized to JSON automatically
    }
}

12. How do you handle different HTTP methods and path variables?

@RestController
@RequestMapping("/api/products")
public class ProductController {

    @GetMapping                              // GET /api/products
    public List<Product> list() { /* ... */ }

    @GetMapping("/{id}")                     // GET /api/products/42
    public Product getById(@PathVariable Long id) { /* ... */ }

    @PostMapping                             // POST /api/products
    public ResponseEntity<Product> create(@Valid @RequestBody ProductDto dto) {
        Product saved = productService.save(dto);
        URI location = URI.create("/api/products/" + saved.getId());
        return ResponseEntity.created(location).body(saved);
    }

    @PutMapping("/{id}")                     // PUT /api/products/42
    public Product update(@PathVariable Long id, @RequestBody ProductDto dto) { /* ... */ }

    @DeleteMapping("/{id}")                  // DELETE /api/products/42
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(@PathVariable Long id) { /* ... */ }
}

13. How do you handle exceptions globally in Spring Boot?

Use @ControllerAdvice or @RestControllerAdvice:

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public Map<String, String> handleNotFound(ResourceNotFoundException ex) {
        return Map.of("error", ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, Object> handleValidation(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors()
            .forEach(e -> errors.put(e.getField(), e.getDefaultMessage()));
        return Map.of("errors", errors);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Map<String, String> handleGeneral(Exception ex) {
        return Map.of("error", "An unexpected error occurred");
    }
}

Spring Boot 3 also provides ProblemDetail (RFC 7807) via ResponseEntityExceptionHandler.


14. How do you validate request bodies?

Add spring-boot-starter-validation and use Bean Validation annotations:

public class CreateUserDto {
    @NotBlank(message = "Name is required")
    private String name;

    @Email(message = "Invalid email format")
    @NotBlank
    private String email;

    @Min(value = 18, message = "Must be at least 18")
    @Max(value = 120)
    private int age;

    @Size(min = 8, message = "Password must be at least 8 characters")
    private String password;
}

@PostMapping("/users")
public User createUser(@Valid @RequestBody CreateUserDto dto) { // @Valid triggers validation
    return userService.create(dto);
}

Validation errors throw MethodArgumentNotValidException, caught by the global handler above.


15. What is @RequestParam vs @PathVariable vs @RequestBody?

// @PathVariable — part of the URL path
@GetMapping("/orders/{orderId}")
public Order getOrder(@PathVariable Long orderId) { /* ... */ }

// @RequestParam — query string parameter (?status=pending&page=1)
@GetMapping("/orders")
public Page<Order> listOrders(
    @RequestParam(defaultValue = "pending") String status,
    @RequestParam(defaultValue = "0") int page) { /* ... */ }

// @RequestBody — request body (JSON → Java object)
@PostMapping("/orders")
public Order createOrder(@RequestBody @Valid CreateOrderDto dto) { /* ... */ }

Data Access

16. What is Spring Data JPA and what are its key abstractions?

Spring Data JPA eliminates boilerplate DAO code. Key interfaces:

Repository (marker)
  └── CrudRepository<T, ID>         — CRUD operations
        └── PagingAndSortingRepository — + pagination & sorting
              └── JpaRepository<T, ID>  — + JPA-specific operations (flush, batch)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    // Derived query — Spring generates SQL from method name
    List<User> findByEmailAndActiveTrue(String email);

    // JPQL query
    @Query("SELECT u FROM User u WHERE u.createdAt > :date")
    List<User> findRecentUsers(@Param("date") LocalDateTime date);

    // Native SQL
    @Query(value = "SELECT * FROM users WHERE role = ?1", nativeQuery = true)
    List<User> findByRoleNative(String role);

    // Custom update
    @Modifying
    @Transactional
    @Query("UPDATE User u SET u.active = false WHERE u.lastLoginAt < :cutoff")
    int deactivateInactiveUsers(@Param("cutoff") LocalDateTime cutoff);
}

17. What is @Transactional and how does it work?

@Transactional wraps a method in a database transaction using AOP proxies:

@Service
public class OrderService {

    @Transactional  // starts transaction before, commits after, rolls back on RuntimeException
    public Order placeOrder(CreateOrderDto dto) {
        Order order = orderRepository.save(new Order(dto));
        inventoryService.deduct(dto.getItems());   // same transaction
        paymentService.charge(dto.getPayment());    // same transaction
        return order;
    }

    @Transactional(readOnly = true)  // optimization: no dirty checking, can use read replicas
    public List<Order> getUserOrders(Long userId) {
        return orderRepository.findByUserId(userId);
    }
}

Key properties:

  • propagation: REQUIRED (default — join or create), REQUIRES_NEW (always new), NESTED
  • isolation: DEFAULT, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
  • rollbackFor: defaults to RuntimeException and Error — add checked exceptions explicitly
  • readOnly = true: performance hint; skips dirty checking

Gotcha: @Transactional only works when called from outside the bean (Spring proxy). Self-invocation (this.method()) bypasses the proxy and the transaction.


18. What is the N+1 problem and how do you fix it?

The N+1 problem: fetching 1 parent entity, then issuing N extra queries for each child collection.

// PROBLEM: 1 query for orders + N queries for each order's items
List<Order> orders = orderRepository.findAll();
orders.forEach(o -> System.out.println(o.getItems().size())); // N lazy loads!

Fixes:

// 1. JOIN FETCH in JPQL
@Query("SELECT o FROM Order o JOIN FETCH o.items WHERE o.userId = :userId")
List<Order> findWithItems(@Param("userId") Long userId);

// 2. EntityGraph
@EntityGraph(attributePaths = {"items", "items.product"})
List<Order> findByUserId(Long userId);

// 3. Batch fetching (Hibernate-specific)
@BatchSize(size = 25)
@OneToMany(mappedBy = "order", fetch = FetchType.LAZY)
private List<OrderItem> items;

Always check generated SQL with spring.jpa.show-sql=true in development.


19. What is the difference between FetchType.LAZY and FetchType.EAGER?

Aspect LAZY EAGER
When loaded On first access Immediately with parent
SQL queries Separate query when accessed JOIN in the same query
Default for @OneToMany LAZY
Default for @ManyToOne EAGER
Risk LazyInitializationException outside session N+1 or large joins
@Entity
public class Author {
    @OneToMany(mappedBy = "author", fetch = FetchType.LAZY)  // good default
    private List<Book> books;

    @ManyToOne(fetch = FetchType.LAZY)  // override EAGER default — usually better
    @JoinColumn(name = "publisher_id")
    private Publisher publisher;
}

Prefer LAZY everywhere; use JOIN FETCH or @EntityGraph when you know you need the relationship.


20. What is Flyway and why use it with Spring Boot?

Flyway manages database schema migrations with versioned SQL scripts:

src/main/resources/db/migration/
├── V1__create_users_table.sql
├── V2__add_email_index.sql
└── V3__add_roles_table.sql
-- V1__create_users_table.sql
CREATE TABLE users (
    id BIGINT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
# application.yml
spring:
  flyway:
    enabled: true
    baseline-on-migrate: true

Spring Boot auto-configures Flyway when it's on the classpath. Migrations run automatically at startup. Alternatives: Liquibase (XML/YAML-based, more flexible rollback support).


Spring Security

21. How does Spring Security work at a high level?

Spring Security uses a chain of Filter objects (the SecurityFilterChain) that intercepts every HTTP request:

Request → DelegatingFilterProxy → FilterChainProxy → [Security Filters] → Servlet

Key filters in order:

  1. SecurityContextPersistenceFilter — loads/saves SecurityContext
  2. UsernamePasswordAuthenticationFilter — processes login form
  3. BasicAuthenticationFilter — processes Authorization: Basic header
  4. BearerTokenAuthenticationFilter — processes JWT Bearer tokens
  5. ExceptionTranslationFilter — converts auth exceptions to HTTP responses
  6. AuthorizationFilter — enforces access control rules

22. How do you configure Spring Security in Spring Boot 3?

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(csrf -> csrf.disable())                          // disable for stateless APIs
            .sessionManagement(s -> s
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/auth/**").permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated())
            .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class);

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config)
            throws Exception {
        return config.getAuthenticationManager();
    }
}

23. How do you implement JWT authentication?

// 1. JWT filter — validates token on every request
@Component
public class JwtAuthFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader("Authorization");
        if (header == null || !header.startsWith("Bearer ")) {
            chain.doFilter(request, response);
            return;
        }

        String token = header.substring(7);
        if (jwtService.isValid(token)) {
            String username = jwtService.extractUsername(token);
            UserDetails user = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken auth =
                new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
            auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(auth);
        }
        chain.doFilter(request, response);
    }
}

// 2. JWT service
@Service
public class JwtService {
    private static final String SECRET = System.getenv("JWT_SECRET");

    public String generateToken(UserDetails user) {
        return Jwts.builder()
            .subject(user.getUsername())
            .issuedAt(new Date())
            .expiration(new Date(System.currentTimeMillis() + 86_400_000))  // 24h
            .signWith(Keys.hmacShaKeyFor(SECRET.getBytes()))
            .compact();
    }

    public boolean isValid(String token) {
        try {
            Jwts.parser().verifyWith(Keys.hmacShaKeyFor(SECRET.getBytes()))
                .build().parseSignedClaims(token);
            return true;
        } catch (JwtException e) {
            return false;
        }
    }
}

24. What is @PreAuthorize and method-level security?

@Configuration
@EnableMethodSecurity  // enables @PreAuthorize / @PostAuthorize / @Secured
public class MethodSecurityConfig {}

@Service
public class ProjectService {

    @PreAuthorize("hasRole('ADMIN') or #userId == authentication.principal.id")
    public Project getProject(Long projectId, Long userId) { /* ... */ }

    @PreAuthorize("hasRole('ADMIN')")
    public void deleteProject(Long id) { /* ... */ }

    @PostAuthorize("returnObject.ownerId == authentication.principal.id")
    public Project findById(Long id) { /* ... */ }
}

Testing

25. What is the difference between @SpringBootTest, @WebMvcTest, and @DataJpaTest?

Annotation Context loaded Use for
@SpringBootTest Full application context Integration tests
@WebMvcTest(MyController.class) MVC layer only (no DB) Controller unit tests
@DataJpaTest JPA layer only (in-memory DB) Repository tests
@RestClientTest REST client + Jackson Client integration tests
@JsonTest Jackson serialization only JSON serialization tests

26. How do you test a REST controller with MockMvc?

@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean                          // mock service layer
    private UserService userService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void getUser_returnsUser() throws Exception {
        User user = new User(1L, "Alice", "alice@example.com");
        when(userService.findById(1L)).thenReturn(user);

        mockMvc.perform(get("/api/users/1")
                .accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(jsonPath("$.name").value("Alice"))
            .andExpect(jsonPath("$.email").value("alice@example.com"));
    }

    @Test
    void createUser_invalidEmail_returnsBadRequest() throws Exception {
        CreateUserDto dto = new CreateUserDto("", "not-an-email", 25, "password123");

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(dto)))
            .andExpect(status().isBadRequest())
            .andExpect(jsonPath("$.errors.email").exists());
    }
}

27. How do you test JPA repositories?

@DataJpaTest  // uses H2 in-memory, rolls back after each test
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private TestEntityManager entityManager;

    @Test
    void findByEmail_existingEmail_returnsUser() {
        User user = new User("Bob", "bob@example.com");
        entityManager.persistAndFlush(user);

        Optional<User> found = userRepository.findByEmail("bob@example.com");

        assertThat(found).isPresent();
        assertThat(found.get().getName()).isEqualTo("Bob");
    }

    @Test
    void findByEmail_nonExistingEmail_returnsEmpty() {
        Optional<User> found = userRepository.findByEmail("ghost@example.com");
        assertThat(found).isEmpty();
    }
}

To test against a real database, add @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE).


Spring Boot Actuator

28. What is Spring Boot Actuator?

Actuator exposes production-ready endpoints for monitoring and management:

# application.yml
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,env,beans,loggers
  endpoint:
    health:
      show-details: always

Key endpoints:

Endpoint What it shows
/actuator/health App health status + DB/cache/disk checks
/actuator/info App version, build info
/actuator/metrics JVM, HTTP, DB metrics
/actuator/env Environment properties
/actuator/beans All Spring beans
/actuator/loggers Log levels (can change at runtime)
/actuator/threaddump Current thread dump
/actuator/httptrace Last HTTP requests

Always secure Actuator endpoints in production — only expose health and info publicly.


29. How do you create a custom health indicator?

@Component
public class ExternalApiHealthIndicator implements HealthIndicator {

    private final RestTemplate restTemplate;

    @Override
    public Health health() {
        try {
            ResponseEntity<String> response =
                restTemplate.getForEntity("https://api.example.com/ping", String.class);

            if (response.getStatusCode().is2xxSuccessful()) {
                return Health.up()
                    .withDetail("responseTime", "< 200ms")
                    .build();
            }
            return Health.down()
                .withDetail("statusCode", response.getStatusCode())
                .build();
        } catch (Exception e) {
            return Health.down(e).build();
        }
    }
}

Configuration

30. What is @ConfigurationProperties and when should you use it?

@ConfigurationProperties binds a prefix from application.yml to a Java class — safer and cleaner than multiple @Value annotations:

# application.yml
app:
  stripe:
    api-key: sk_test_xxx
    webhook-secret: whsec_xxx
    max-retries: 3
  rate-limit:
    requests-per-minute: 100
@ConfigurationProperties(prefix = "app.stripe")
@Validated
public class StripeProperties {
    @NotBlank
    private String apiKey;
    @NotBlank
    private String webhookSecret;
    @Min(1) @Max(10)
    private int maxRetries = 3;
    // getters/setters or use Lombok @Data
}

@SpringBootApplication
@EnableConfigurationProperties(StripeProperties.class)
public class App { /* ... */ }

31. How do profiles work in Spring Boot?

Profiles let you have environment-specific configuration:

src/main/resources/
├── application.yml             (shared config)
├── application-dev.yml         (development overrides)
├── application-staging.yml     (staging overrides)
└── application-prod.yml        (production overrides)
# application.yml
spring:
  profiles:
    active: dev  # default — override with env var SPRING_PROFILES_ACTIVE=prod

# application-prod.yml
spring:
  datasource:
    url: jdbc:postgresql://prod-db:5432/myapp
logging:
  level:
    root: WARN

Activate via:

  • Environment variable: SPRING_PROFILES_ACTIVE=prod
  • JVM arg: -Dspring.profiles.active=prod
  • @ActiveProfiles("test") in tests

Use @Profile("dev") to conditionally register beans:

@Bean
@Profile("dev")
public DataSource h2DataSource() { /* in-memory for dev */ }

Microservices

32. How do Spring Boot microservices communicate?

Synchronous (REST/gRPC):

// RestTemplate (older approach)
RestTemplate rest = new RestTemplate();
User user = rest.getForObject("http://user-service/users/{id}", User.class, userId);

// WebClient (reactive, non-blocking — preferred in Spring Boot 3)
WebClient webClient = WebClient.create("http://user-service");
Mono<User> userMono = webClient.get()
    .uri("/users/{id}", userId)
    .retrieve()
    .bodyToMono(User.class);

// Feign Client (declarative, from Spring Cloud OpenFeign)
@FeignClient(name = "user-service")
public interface UserClient {
    @GetMapping("/users/{id}")
    User getUser(@PathVariable Long id);
}

Asynchronous (messaging):

// Kafka producer
kafkaTemplate.send("orders.created", orderId.toString(), orderEvent);

// Kafka consumer
@KafkaListener(topics = "orders.created", groupId = "notification-service")
public void handleOrderCreated(OrderCreatedEvent event) { /* ... */ }

33. What is the Circuit Breaker pattern and how does Resilience4j implement it?

A Circuit Breaker prevents cascading failures by "opening" when a downstream service fails repeatedly:

CLOSED → normal flow, counts failures
  ↓ (failure threshold exceeded)
OPEN → all calls fail fast (no calls made to downstream)
  ↓ (after wait duration)
HALF_OPEN → allow probe calls
  ↓ (probe succeeds)       ↓ (probe fails)
CLOSED                    OPEN
// application.yml
resilience4j.circuitbreaker:
  instances:
    paymentService:
      failure-rate-threshold: 50        # open after 50% failure rate
      wait-duration-in-open-state: 30s  # wait 30s before half-open
      permitted-number-of-calls-in-half-open-state: 5

// Usage
@Service
public class OrderService {
    @CircuitBreaker(name = "paymentService", fallbackMethod = "paymentFallback")
    public PaymentResult processPayment(PaymentRequest request) {
        return paymentClient.charge(request);
    }

    private PaymentResult paymentFallback(PaymentRequest request, Exception ex) {
        return PaymentResult.queued("Payment queued for retry");
    }
}

34. What is Spring Cloud Config?

Spring Cloud Config externalizes configuration for microservices, serving properties from a Git repository:

# Config server
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/myorg/config-repo
          search-paths: '{application}'

# Client service bootstrap.yml
spring:
  application:
    name: order-service
  config:
    import: "configserver:http://config-server:8888"

Benefits: centralized config, environment-specific profiles, encryption for secrets, live refresh via /actuator/refresh + Spring Cloud Bus.


Advanced Topics

35. What is the difference between @Async and CompletableFuture?

@Configuration
@EnableAsync
public class AsyncConfig {
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(25);
        executor.setThreadNamePrefix("async-");
        executor.initialize();
        return executor;
    }
}

@Service
public class EmailService {
    @Async  // runs in executor thread pool, not caller's thread
    public CompletableFuture<Void> sendWelcomeEmail(String to) {
        // ... send email
        return CompletableFuture.completedFuture(null);
    }
}

// Combine multiple async operations
CompletableFuture<Void> email = emailService.sendWelcomeEmail(user.getEmail());
CompletableFuture<Void> sms = smsService.sendWelcomeSms(user.getPhone());
CompletableFuture.allOf(email, sms).join();  // wait for both

Gotcha: @Async self-invocation doesn't work (same proxy issue as @Transactional).


36. What is Spring Boot's embedded server and how do you switch from Tomcat to Undertow?

Spring Boot ships with embedded Tomcat by default. Switch to Undertow (lower memory, better for reactive):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

37. What is Spring Boot DevTools?

DevTools provides developer conveniences:

  • Automatic restart: application restarts when classpath changes (faster than full restart via two ClassLoaders)
  • LiveReload: browser refreshes on static resource changes
  • Property defaults: disables caching for Thymeleaf, Freemarker, etc.
  • H2 console: automatically enabled when H2 is on classpath
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
    <optional>true</optional>  <!-- excluded from production builds -->
</dependency>

38. How do you implement caching in Spring Boot?

@SpringBootApplication
@EnableCaching
public class App { /* ... */ }

// application.yml
spring:
  cache:
    type: redis  # caffeine, simple, ehcache

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")  // cache result on first call
    public Product findById(Long id) {
        return productRepository.findById(id).orElseThrow();
    }

    @CachePut(value = "products", key = "#product.id")  // always updates cache
    public Product update(Product product) {
        return productRepository.save(product);
    }

    @CacheEvict(value = "products", key = "#id")  // removes from cache
    public void delete(Long id) {
        productRepository.deleteById(id);
    }

    @CacheEvict(value = "products", allEntries = true)  // clears entire cache
    public void clearAll() {}
}

39. What is @Scheduled and how do you use it?

@SpringBootApplication
@EnableScheduling
public class App { /* ... */ }

@Component
public class ReportScheduler {

    @Scheduled(cron = "0 0 6 * * MON-FRI")  // 6 AM Mon-Fri
    public void sendDailyReport() { /* ... */ }

    @Scheduled(fixedRate = 30_000)           // every 30 seconds
    public void pollExternalApi() { /* ... */ }

    @Scheduled(fixedDelay = 60_000, initialDelay = 5_000)  // 60s after last completion
    public void cleanupTempFiles() { /* ... */ }
}

For distributed environments use Spring Batch, Quartz, or ShedLock to prevent concurrent execution across multiple instances.


40. How does Spring Boot handle transactions across microservices?

Distributed transactions are complex — avoid XA/2PC when possible. Common patterns:

Pattern How When
Saga (Choreography) Each service publishes events; others react Loose coupling preferred
Saga (Orchestration) Central orchestrator calls services Clear workflow, easier debugging
Outbox Pattern Write event to same DB as entity; relay picks up and publishes Exactly-once delivery
Two-Phase Commit (XA) Distributed lock across DBs Avoid — poor scalability
// Outbox pattern — atomic write + reliable publish
@Transactional
public Order createOrder(CreateOrderDto dto) {
    Order order = orderRepository.save(new Order(dto));
    // Write to outbox IN SAME TRANSACTION
    outboxRepository.save(new OutboxEvent("order.created", order.getId(), toJson(order)));
    return order;
}
// Separate poller reads outbox and publishes to Kafka

Performance & Production

41. How do you optimize Spring Boot startup time?

spring:
  main:
    lazy-initialization: true          # initialize beans on first use
  jmx:
    enabled: false                     # disable JMX if not needed

Other techniques:

  • Use Spring AOT (Ahead-of-Time) compilation with GraalVM Native Image
  • Remove unused starters from classpath
  • Use @ImportAutoConfiguration instead of full @SpringBootApplication component scan
  • Profile startup with -Dspring-boot.run.jvmArguments="-verbose:class"

42. What is Spring Boot's support for GraalVM Native Image?

Spring Boot 3 has first-class support for GraalVM Native Image via Spring AOT:

# With Maven
./mvnw -Pnative native:compile

# With Gradle
./gradlew nativeCompile

Benefits: instant startup (~50ms), reduced memory (~10x less), smaller Docker images. Limitations: longer build time, reflection/dynamic proxies need hints, no runtime class loading.

// Register reflection hints for classes used dynamically
@NativeHint(
    reflection = @ReflectionHint(types = MyClass.class, memberCategories = MemberCategory.INVOKE_DECLARED_METHODS)
)
public class MyRuntimeHints implements RuntimeHintsRegistrar { /* ... */ }

43. How do you configure connection pooling in Spring Boot?

Spring Boot uses HikariCP by default (fastest JVM connection pool):

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/myapp
    username: ${DB_USER}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20         # max connections (default: 10)
      minimum-idle: 5               # min idle connections
      connection-timeout: 30000     # ms to wait for connection
      idle-timeout: 600000          # ms before idle connection is removed
      max-lifetime: 1800000         # ms max connection lifetime
      pool-name: MyAppHikariCP

Rule of thumb: max-pool-size = (num_cores * 2) + num_spindles. More connections ≠ better performance.


Common Mistakes

Mistake Problem Fix
Field injection with @Autowired Hard to unit test, hides dependencies Use constructor injection
@Transactional self-invocation Proxy bypassed, no transaction Use @Transactional on public methods called from outside
FetchType.EAGER everywhere Unnecessary joins, memory bloat Default to LAZY, JOIN FETCH when needed
Exposing all Actuator endpoints Security risk Only expose health, info publicly
Storing sensitive values in application.properties Secrets in version control Use env vars, Vault, or Config Server
@SpringBootTest for every test Slow test suite Use @WebMvcTest, @DataJpaTest for slices
Not using @ConfigurationProperties Scattered @Value, no validation Group related config with @ConfigurationProperties
Hard-coding localhost in service URLs Breaks in Kubernetes Use service names + Feign/service discovery

Spring Boot vs alternatives

Aspect Spring Boot Quarkus Micronaut Vert.x
Startup time Moderate (JVM) Fast (native-first) Fast (compile-time DI) Very fast
Memory ~200–400MB ~50–100MB native ~100–200MB ~50MB
Ecosystem Largest Growing Growing Smaller
Native image Via GraalVM (Boot 3) Native-first Native-first Limited
Learning curve High (rich feature set) Moderate Moderate Steep (reactive)
Best for Enterprise apps, existing Java teams Cloud-native microservices Microservices High-concurrency I/O

Frequently asked questions

Q: When should you use Spring WebFlux instead of Spring MVC?
Use WebFlux for I/O-heavy workloads (thousands of concurrent connections, streaming data, reactive databases like R2DBC). For typical CRUD APIs with <1000 req/s, Spring MVC + virtual threads (Java 21) is simpler and sufficient.

Q: What is the difference between spring.jpa.generate-ddl and spring.jpa.hibernate.ddl-auto?
generate-ddl is a generic Spring Data flag; ddl-auto is Hibernate-specific with more options (validate, update, create, create-drop, none). In production always use none and manage schema with Flyway/Liquibase.

Q: How do you handle circular dependencies?
Circular dependencies (A → B → A) indicate a design problem. Fix by: extracting shared logic to a third bean, using @Lazy on one injection point (breaks the cycle at runtime), or redesigning the component boundaries.

Q: What is the difference between @MockBean and @Mock?
@Mock (Mockito) creates a mock without Spring context — use in pure unit tests. @MockBean (Spring Boot Test) creates a mock AND registers it in the Spring ApplicationContext, replacing any existing bean — use in @SpringBootTest / @WebMvcTest slice tests.

Q: How do you run multiple datasources in Spring Boot?
Disable auto-configuration for datasources and configure each manually with its own DataSource, EntityManagerFactory, and TransactionManager beans, using @Primary to mark the default and @Qualifier to distinguish them.

Q: What is Spring Boot's default JSON serialization library and how do you customize it?
Jackson is the default. Customize via JacksonAutoConfiguration properties or a custom ObjectMapper bean:

@Bean
public ObjectMapper objectMapper() {
    return new ObjectMapper()
        .setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .registerModule(new JavaTimeModule());
}

Keep reading

All Toolmingotools are free & run in your browser

No sign-up, no upload, no watermark. Your files never leave your device.

Browse all tools