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):
- Spring Boot scans classpath for jars.
- Each jar can declare auto-configuration classes in
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. - 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),NESTEDisolation:DEFAULT,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLErollbackFor: defaults toRuntimeExceptionandError— add checked exceptions explicitlyreadOnly = 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:
SecurityContextPersistenceFilter— loads/savesSecurityContextUsernamePasswordAuthenticationFilter— processes login formBasicAuthenticationFilter— processesAuthorization: BasicheaderBearerTokenAuthenticationFilter— processes JWT Bearer tokensExceptionTranslationFilter— converts auth exceptions to HTTP responsesAuthorizationFilter— 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
@ImportAutoConfigurationinstead of full@SpringBootApplicationcomponent 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());
}