0% found this document useful (0 votes)
5 views73 pages

Spring Boot Interview Questions Guide: For 8+ Years Experienced Java Developers

This document is a comprehensive guide for Spring Boot interview questions tailored for Java developers with over 8 years of experience. It covers core concepts, new features in Spring Boot 3.x, auto-configuration, dependency management, RESTful API design, microservices architecture, and best practices. The guide includes detailed explanations, code examples, and various strategies for effective Spring Boot application development and deployment.

Uploaded by

kiransai3699
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views73 pages

Spring Boot Interview Questions Guide: For 8+ Years Experienced Java Developers

This document is a comprehensive guide for Spring Boot interview questions tailored for Java developers with over 8 years of experience. It covers core concepts, new features in Spring Boot 3.x, auto-configuration, dependency management, RESTful API design, microservices architecture, and best practices. The guide includes detailed explanations, code examples, and various strategies for effective Spring Boot application development and deployment.

Uploaded by

kiransai3699
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

Spring Boot Interview

Questions Guide
For 8+ Years Experienced Java
Developers
Comprehensive Interview Preparation Resource
Updated: March 2026

Table of Contents
1. Core Spring Boot Concepts
2. Spring Boot 3.x New Features
3. Auto-Configuration and Dependency Management
4. RESTful API Design and Best Practices
5. Microservices Architecture
6. Spring Boot Security
7. Data Access and JPA
8. Transaction Management
9. Performance Optimization
10. Messaging and Event-Driven Architecture
11. Monitoring, Observability, and Actuator
12. Testing Strategies
13. Deployment and DevOps
14. Design Patterns and Best Practices
15. Scenario-Based Questions

1. Core Spring Boot Concepts


Q1. What is Spring Boot and how does it differ
from the traditional Spring Framework?
Answer:

Spring Boot is an opinionated framework built on top of the Spring Framework


that simplifies the configuration and deployment of Spring applications[1].

Key Differences:

Aspect Spring Framework Spring Boot

Extensive XML or Java Auto-configuration with


Configuration
configuration required minimal setup
Requires external Embedded server
Deployment server (Tomcat, JBoss) (Tomcat, Jetty,
Undertow)
Dependency Manual dependency Starter dependencies
Management management with curated versions
Requires additional Built-in Actuator for
Production Ready
setup monitoring
Longer setup and Rapid application
Development Time
configuration development

Table 1: Spring Framework vs Spring Boot Comparison

Q2. Explain the @SpringBootApplication


annotation and its components.
Answer:

@SpringBootApplication is a meta-annotation that combines three important


annotations:

• @Configuration: Marks the class as a source of bean definitions


• @EnableAutoConfiguration: Enables Spring Boot's auto-configuration
mechanism
• @ComponentScan: Enables component scanning in the current package
and sub-packages
Example:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}

Q3. How does Spring Boot's auto-


configuration work internally?
Answer:

Spring Boot's auto-configuration uses the following mechanism[1]:

1. @EnableAutoConfiguration triggers the auto-configuration process


2. Spring Boot scans the classpath for [Link] files (Spring Boot
2.x) or
META-INF/spring/[Link]
[Link] (Spring Boot 3.x)
3. Conditional annotations determine which configurations to apply:
• @ConditionalOnClass - checks if specific classes are on classpath
• @ConditionalOnMissingBean - applies if bean is not already
defined
• @ConditionalOnProperty - checks for specific properties
4. Configuration classes create beans based on conditions met
5. User-defined beans take precedence over auto-configured beans
Example of Custom Auto-Configuration:
@Configuration
@ConditionalOnClass([Link])
@ConditionalOnProperty(name = "[Link]", havingValue =
"true")
public class CustomDataSourceAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
return new HikariDataSource();
}

Q4. What are Spring Boot Starters and how do


they work?
Answer:

Starters are dependency descriptors that bring in all necessary dependencies for
a specific functionality with compatible versions.

Common Starters:

Starter Purpose

Web applications with Spring MVC,


spring-boot-starter-web
Tomcat, Jackson
spring-boot-starter-data-jpa JPA with Hibernate
spring-boot-starter-security Spring Security
spring-boot-starter-test Testing with JUnit, Mockito, AssertJ
Production monitoring and
spring-boot-starter-actuator
management
spring-boot-starter-cache Caching abstraction
Bean Validation with Hibernate
spring-boot-starter-validation
Validator

Table 2: Common Spring Boot Starters

2. Spring Boot 3.x New Features


Q5. What are the major changes in Spring
Boot 3.x?
Answer:

Spring Boot 3.x introduced significant changes[11]:

1. Java 17 Baseline: Minimum Java version is 17, supporting modern Java


features
2. Jakarta EE 9+: Migration from javax.* to jakarta.* namespace
3. Native Image Support: Enhanced GraalVM native compilation support
4. Observability Improvements: Better integration with Micrometer and
OpenTelemetry
5. HTTP Interface Clients: Declarative HTTP clients similar to Spring
Cloud OpenFeign
6. Problem Details (RFC 7807): Standardized error responses
7. Spring Native: Production-ready native image support
Migration Example:
// Spring Boot 2.x
import [Link];
import [Link];

// Spring Boot 3.x


import [Link];
import [Link];

Q6. Explain Spring Boot's native image


support with GraalVM.
Answer:

Spring Native enables ahead-of-time (AOT) compilation of Spring applications to


native executables using GraalVM[11].

Benefits:

• Instant Startup: Applications start in milliseconds


• Reduced Memory Footprint: Lower memory consumption
• Smaller Image Size: Optimized for containerized environments
• No JVM Required: Self-contained executable
Configuration:
[Link] native-maven-plugin

Limitations:

 Reflection, dynamic proxies require configuration


 Not all libraries are compatible
 Longer build times

Q7. What is the HTTP Interface Client in


Spring Boot 3?
Answer:

HTTP Interface allows declaring HTTP services as Java interfaces, similar to


Spring Data repositories:

public interface UserClient {

@GetExchange("/users/{id}")
User getUserById(@PathVariable Long id);

@PostExchange("/users")
User createUser(@RequestBody User user);

@DeleteExchange("/users/{id}")
void deleteUser(@PathVariable Long id);

Configuration:
@Configuration
public class HttpClientConfig {

@Bean
public UserClient userClient() {
WebClient webClient = [Link]()
.baseUrl("[Link]
.build();

HttpServiceProxyFactory factory = HttpServiceProxyFactory


.builder([Link](webClient))
.build();

return [Link]([Link]);
}

3. Auto-Configuration and
Dependency Management
Q8. How do you exclude specific auto-
configuration classes?
Answer:

Multiple approaches to exclude auto-configurations:

1. Using @SpringBootApplication:
@SpringBootApplication(exclude = {
[Link],
[Link]
})
public class MyApplication {
}

2. Using [Link]:
[Link]=
[Link],
[Link]
n

3. Using @EnableAutoConfiguration:
@Configuration
@EnableAutoConfiguration(exclude = {[Link]})
public class AppConfig {
}

Q9. How do you create a custom Spring Boot


Starter?
Answer:
Steps to create a custom starter:

1. Create a new Maven/Gradle project with naming convention: spring-


boot-starter-[name]
2. Create auto-configuration class:
@Configuration
@ConditionalOnClass([Link])
@EnableConfigurationProperties([Link])
public class MyServiceAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties);
}

3. Create properties class:


@ConfigurationProperties(prefix = "myservice")
public class MyServiceProperties {
private String endpoint;
private int timeout = 5000;
// getters and setters
}

4. Register auto-configuration in
META-INF/spring/[Link]
[Link]:
[Link]

Q10. Explain the Spring Boot dependency


management and version resolution strategy.
Answer:

Spring Boot uses a BOM (Bill of Materials) approach:

• spring-boot-dependencies defines versions for all managed


dependencies
• Parent POM spring-boot-starter-parent inherits from dependencies
BOM
• Version conflicts are resolved by the BOM, ensuring compatibility
• Override versions using properties:
<[Link]>2.15.0</[Link]>
Dependency Management Hierarchy:
spring-boot-starter-parent
└── spring-boot-dependencies (BOM)
└── Managed Dependencies (400+)

4. RESTful API Design and Best


Practices
Q11. How do you implement API versioning in
Spring Boot?
Answer:

Four common approaches for API versioning[2]:

1. URI Versioning (Most Common):


@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
@GetMapping("/{id}")
public UserV1 getUser(@PathVariable Long id) {
return userService.getUserV1(id);
}
}

@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
@GetMapping("/{id}")
public UserV2 getUser(@PathVariable Long id) {
return userService.getUserV2(id);
}
}

2. Header Versioning:
@RestController
@RequestMapping("/api/users")
public class UserController {

@GetMapping(value = "/{id}", headers = "API-VERSION=1")


public UserV1 getUserV1(@PathVariable Long id) {
return userService.getUserV1(id);
}

@GetMapping(value = "/{id}", headers = "API-VERSION=2")


public UserV2 getUserV2(@PathVariable Long id) {
return userService.getUserV2(id);
}
}

3. Query Parameter Versioning:


@GetMapping(value = "/users/{id}", params = "version=1")
public UserV1 getUserV1(@PathVariable Long id) {
return userService.getUserV1(id);
}

4. Content Negotiation (Accept Header):


@GetMapping(value = "/{id}", produces = "application/[Link].v1+json")
public UserV1 getUserV1(@PathVariable Long id) {
return userService.getUserV1(id);
}

Q12. How do you implement global exception


handling in Spring Boot?
Answer:

Use @ControllerAdvice with @ExceptionHandler:

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler([Link])
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFound(
ResourceNotFoundException ex,
WebRequest request) {
[Link]("Resource not found: {}", [Link]());
return [Link]()
.timestamp([Link]())
.status(HttpStatus.NOT_FOUND.value())
.error("Resource Not Found")
.message([Link]())
.path([Link](false))
.build();
}

@ExceptionHandler([Link])
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ValidationErrorResponse handleValidationException(
MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
[Link]().getFieldErrors().forEach(error ->
[Link]([Link](), [Link]())
);
return new ValidationErrorResponse(
[Link](),
HttpStatus.BAD_REQUEST.value(),
"Validation Failed",
errors
);
}

@ExceptionHandler([Link])
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGlobalException(Exception ex) {
[Link]("Unexpected error occurred", ex);
return [Link]()
.timestamp([Link]())
.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
.error("Internal Server Error")
.message("An unexpected error occurred")
.build();
}

Q13. Explain REST API best practices for


handling large datasets with pagination.
Answer:

Implement pagination using Spring Data's Pageable:

@RestController
@RequestMapping("/api/users")
public class UserController {

@GetMapping
public ResponseEntity<PagedResponse<UserDTO>> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size,
@RequestParam(defaultValue = "id,asc") String[] sort) {

// Parse sort parameters


List<[Link]> orders = [Link](sort)
.map(s -> {
String[] parts = [Link](",");
return new [Link](
[Link](parts[1]),
parts[0]
);
})
.collect([Link]());

Pageable pageable = [Link](page, size, [Link](orders));


Page<User> userPage = [Link](pageable);
PagedResponse<UserDTO> response =
PagedResponse.<UserDTO>builder()
.content([Link]().stream()
.map(userMapper::toDTO)
.collect([Link]()))
.page([Link]())
.size([Link]())
.totalElements([Link]())
.totalPages([Link]())
.last([Link]())
.build();

return [Link](response);
}

Best Practices:

• Use cursor-based pagination for real-time data


• Implement Link headers for navigation (HATEOAS)
• Set reasonable default and maximum page sizes
• Use database-level pagination (LIMIT/OFFSET)
• Cache paginated results when appropriate
• Consider using Slice instead of Page for performance (no count query)

Q14. How do you implement HATEOAS in


Spring Boot?
Answer:

HATEOAS (Hypermedia as the Engine of Application State) adds links to REST


responses:

@RestController
@RequestMapping("/api/users")
public class UserController {

@GetMapping("/{id}")
public EntityModel<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = [Link](id);

EntityModel<UserDTO> resource = [Link](user);

// Add self link


[Link](linkTo(methodOn([Link])
.getUser(id)).withSelfRel());
// Add related links
[Link](linkTo(methodOn([Link])
.getUserOrders(id)).withRel("orders"));
[Link](linkTo(methodOn([Link])
.updateUser(id, null)).withRel("update"));
[Link](linkTo(methodOn([Link])
.deleteUser(id)).withRel("delete"));

return resource;
}

@GetMapping
public CollectionModel<EntityModel<UserDTO>> getAllUsers() {
List<EntityModel<UserDTO>> users = [Link]()
.stream()
.map(user -> [Link](user,
linkTo(methodOn([Link])
.getUser([Link]())).withSelfRel()))
.collect([Link]());

return [Link](users,
linkTo(methodOn([Link])
.getAllUsers()).withSelfRel());
}

Dependency:
[Link] spring-boot-starter-hateoas

5. Microservices Architecture
Q15. How do you design a microservices
architecture using Spring Boot?
Answer:

Key components and patterns for microservices architecture[2]:

1. Service Discovery (Eureka, Consul)


2. API Gateway (Spring Cloud Gateway)
3. Configuration Management (Spring Cloud Config)
4. Circuit Breaker (Resilience4j)
5. Distributed Tracing (Zipkin, Jaeger)
6. Load Balancing (Spring Cloud LoadBalancer)
7. Message Broker (Kafka, RabbitMQ)
Architecture Diagram Components:

Component Responsibility

Single entry point, routing,


API Gateway
authentication, rate limiting
Service discovery and health
Service Registry
monitoring
Centralized configuration
Config Server
management
Fault tolerance and fallback
Circuit Breaker
mechanisms
Asynchronous communication
Message Bus
between services
Distributed Tracing Request tracking across services

Table 3: Microservices Components

Q16. Explain the implementation of Circuit


Breaker pattern using Resilience4j.
Answer:

Circuit Breaker prevents cascading failures in microservices:

Configuration:
resilience4j:
circuitbreaker:
instances:
userService:
registerHealthIndicator: true
slidingWindowSize: 10
minimumNumberOfCalls: 5
permittedNumberOfCallsInHalfOpenState: 3
automaticTransitionFromOpenToHalfOpenEnabled: true
waitDurationInOpenState: 5s
failureRateThreshold: 50
slowCallRateThreshold: 100
slowCallDurationThreshold: 2s

Implementation:
@Service
@Slf4j
public class OrderService {
@Autowired
private UserServiceClient userServiceClient;

@CircuitBreaker(name = "userService", fallbackMethod = "getUserFallback")


@Retry(name = "userService")
@TimeLimiter(name = "userService")
public CompletableFuture<User> getUser(Long userId) {
return [Link](() ->
[Link](userId));
}

private CompletableFuture<User> getUserFallback(


Long userId,
Exception ex) {
[Link]("Fallback triggered for user: {}, reason: {}",
userId, [Link]());
return [Link](
[Link]()
.id(userId)
.name("Anonymous")
.status("UNAVAILABLE")
.build()
);
}

States:

 CLOSED: Normal operation


 OPEN: Failing, all requests fail fast
 HALF_OPEN: Testing if service recovered

Q17. How do you implement distributed


tracing in Spring Boot microservices?
Answer:

Use Spring Cloud Sleuth with Zipkin/Jaeger for distributed tracing:

Dependencies:
[Link] micrometer-tracing-bridge-brave [Link].reporter2 zipkin-
reporter-brave

Configuration:
management:
tracing:
sampling:
probability: 1.0
zipkin:
tracing:
endpoint: [Link]

Custom Spans:
@Service
public class OrderService {

@Autowired
private Tracer tracer;

public Order processOrder(OrderRequest request) {


Span customSpan = [Link]().name("process-order");

try ([Link] ws = [Link]([Link]())) {


[Link]("[Link]", [Link]().toString());
[Link]("[Link]", [Link]().toString());

// Business logic
Order order = createOrder(request);

[Link]("result", "success");
return order;
} catch (Exception e) {
[Link]("error", [Link]());
throw e;
} finally {
[Link]();
}
}

Q18. Explain the Saga pattern for distributed


transactions.
Answer:

The Saga pattern manages distributed transactions using a sequence of local


transactions with compensating transactions:

Two Implementation Approaches:

1. Choreography-Based Saga (Event-Driven):


@Service
public class OrderSaga {

@Autowired
private KafkaTemplate<String, Object> kafkaTemplate;

@Transactional
public void createOrder(OrderRequest request) {
// Local transaction 1: Create order
Order order = [Link](new Order(request));

// Publish event for next step


[Link]("order-created",
new OrderCreatedEvent([Link](), [Link]()));
}

@KafkaListener(topics = "payment-completed")
public void handlePaymentCompleted(PaymentCompletedEvent event) {
Order order = [Link]([Link]())
.orElseThrow();
[Link](OrderStatus.PAYMENT_COMPLETED);
[Link](order);

// Continue saga
[Link]("reserve-inventory",
new ReserveInventoryEvent([Link](), [Link]()));
}

@KafkaListener(topics = "payment-failed")
public void handlePaymentFailed(PaymentFailedEvent event) {
// Compensating transaction
Order order = [Link]([Link]())
.orElseThrow();
[Link]([Link]);
[Link](order);

// Notify user
[Link]("order-cancelled",
new OrderCancelledEvent([Link]()));
}

2. Orchestration-Based Saga (Centralized Coordinator):


@Service
public class OrderOrchestrator {

@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;
@Autowired
private ShippingService shippingService;

public Order executeOrderSaga(OrderRequest request) {


Order order = createOrder(request);

try {
// Step 1: Process payment
Payment payment = [Link](
[Link](), [Link]());

// Step 2: Reserve inventory


Reservation reservation = [Link](
[Link]());

// Step 3: Arrange shipping


Shipment shipment = [Link](
[Link](), [Link]());

[Link]([Link]);
return [Link](order);

} catch (PaymentException e) {
cancelOrder(order);
throw new OrderException("Payment failed", e);
} catch (InventoryException e) {
compensatePayment(order);
cancelOrder(order);
throw new OrderException("Inventory unavailable", e);
} catch (ShippingException e) {
compensateInventory(order);
compensatePayment(order);
cancelOrder(order);
throw new OrderException("Shipping failed", e);
}
}

private void compensatePayment(Order order) {


[Link]([Link]());
}

private void compensateInventory(Order order) {


[Link]([Link]());
}

6. Spring Boot Security


Q19. How do you implement JWT
authentication in Spring Boot 3?
Answer:
Implementation of JWT authentication with Spring Security 6[13]:

JWT Utility Class:


@Component
public class JwtTokenProvider {

@Value("${[Link]}")
private String jwtSecret;

@Value("${[Link]}")
private long jwtExpiration;

private Key getSigningKey() {


byte[] keyBytes = [Link](jwtSecret);
return [Link](keyBytes);
}

public String generateToken(UserDetails userDetails) {


Map<String, Object> claims = new HashMap<>();
[Link]("roles", [Link]().stream()
.map(GrantedAuthority::getAuthority)
.collect([Link]()));

return [Link]()
.setClaims(claims)
.setSubject([Link]())
.setIssuedAt(new Date())
.setExpiration(new Date([Link]() + jwtExpiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}

public String extractUsername(String token) {


return extractClaim(token, Claims::getSubject);
}

public <T> T extractClaim(String token, Function<Claims, T> claimsResolver)


{
final Claims claims = extractAllClaims(token);
return [Link](claims);
}

private Claims extractAllClaims(String token) {


return [Link]()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}

public boolean validateToken(String token, UserDetails userDetails) {


final String username = extractUsername(token);
return [Link]([Link]())
&& !isTokenExpired(token);
}

private boolean isTokenExpired(String token) {


return extractExpiration(token).before(new Date());
}

private Date extractExpiration(String token) {


return extractClaim(token, Claims::getExpiration);
}

JWT Authentication Filter:


@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

@Autowired
private JwtTokenProvider tokenProvider;

@Autowired
private UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {

String token = extractTokenFromRequest(request);

if (token != null && [Link]()


.getAuthentication() == null) {
try {
String username = [Link](token);
UserDetails userDetails = userDetailsService
.loadUserByUsername(username);

if ([Link](token, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails,
null,
[Link]()
);
[Link](
new WebAuthenticationDetailsSource()
.buildDetails(request)
);
[Link]()
.setAuthentication(authentication);
}
} catch (Exception e) {
[Link]("Cannot set user authentication", e);
}
}

[Link](request, response);
}

private String extractTokenFromRequest(HttpServletRequest request) {


String bearerToken = [Link]("Authorization");
if ([Link](bearerToken)
&& [Link]("Bearer ")) {
return [Link](7);
}
return null;
}

Security Configuration:
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

@Autowired
private JwtAuthenticationFilter jwtAuthFilter;

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.csrf(csrf -> [Link]())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy([Link])
)
.addFilterBefore(jwtAuthFilter,
[Link]);

return [Link]();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration config) throws Exception {
return [Link]();
}

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

Q20. Explain OAuth 2.0 implementation in


Spring Boot.
Answer:

OAuth 2.0 provides delegated authorization with different grant types[13]:

Resource Server Configuration:


@Configuration
@EnableWebSecurity
public class ResourceServerConfig {

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)
throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/user/**")
.hasAuthority("SCOPE_user.read")
.requestMatchers("/api/admin/**")
.hasAuthority("SCOPE_admin")
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> [Link](
jwtAuthenticationConverter()))
);

return [Link]();
}

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter =
new JwtGrantedAuthoritiesConverter();
[Link]("roles");
[Link]("ROLE_");

JwtAuthenticationConverter jwtAuthenticationConverter =
new JwtAuthenticationConverter();
[Link](
grantedAuthoritiesConverter);

return jwtAuthenticationConverter;
}

Configuration:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: [Link]
jwk-set-uri: [Link]

Q21. How do you implement method-level


security in Spring Boot?
Answer:

Use @PreAuthorize, @PostAuthorize, @Secured annotations:

@Service
public class UserService {

@PreAuthorize("hasRole('ADMIN')")
public List<User> getAllUsers() {
return [Link]();
}

@PreAuthorize("hasRole('USER') or hasRole('ADMIN')")
public User getUserById(Long id) {
return [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}

@PreAuthorize("#username == [Link] or
hasRole('ADMIN')")
public User updateUser(String username, UserUpdateRequest request) {
User user = [Link](username)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
// Update logic
return [Link](user);
}
@PostAuthorize("[Link] ==
[Link]")
public User loadUserDetails(Long id) {
return [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}

@PreAuthorize("@[Link](#userId)")
public User getUser(Long userId) {
return [Link](userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}

@Component("userSecurity")
public class UserSecurityService {

public boolean canAccessUser(Long userId) {


Authentication authentication = SecurityContextHolder
.getContext().getAuthentication();

if (authentication == null || ![Link]()) {


return false;
}

UserDetails userDetails = (UserDetails) [Link]();


// Custom logic to check if user can access resource
return true;
}

7. Data Access and JPA


Q22. Explain the N+1 query problem and how
to solve it in Spring Data JPA.
Answer:

The N+1 problem occurs when fetching a collection with lazy-loaded


associations, resulting in 1 query for the parent and N queries for children.

Problem Example:
// This causes N+1 queries
List<Department> departments = [Link]();
[Link](dept -> {
[Link]([Link]().size()); // Triggers N queries
});

Solutions:

1. Entity Graph:
@Entity
public class Department {
@Id
private Long id;

@OneToMany(mappedBy = "department", fetch = [Link])


private List<Employee> employees;

public interface DepartmentRepository extends JpaRepository<Department,


Long> {

@EntityGraph(attributePaths = {"employees"})
List<Department> findAll();

@EntityGraph(attributePaths = {"employees", "[Link]"})


Optional<Department> findById(Long id);

2. JPQL JOIN FETCH:


public interface DepartmentRepository extends JpaRepository<Department,
Long> {

@Query("SELECT DISTINCT d FROM Department d LEFT JOIN FETCH


[Link]")
List<Department> findAllWithEmployees();

@Query("SELECT d FROM Department d " +


"LEFT JOIN FETCH [Link] e " +
"LEFT JOIN FETCH [Link] " +
"WHERE [Link] = :id")
Optional<Department> findByIdWithEmployeesAndSkills(@Param("id") Long
id);

3. Batch Fetching:
@Entity
public class Department {
@Id
private Long id;
@OneToMany(mappedBy = "department")
@BatchSize(size = 10)
private List<Employee> employees;

4. DTO Projection:
public interface DepartmentRepository extends JpaRepository<Department,
Long> {

@Query("SELECT new [Link](" +


"[Link], [Link], COUNT(e)) " +
"FROM Department d LEFT JOIN [Link] e " +
"GROUP BY [Link], [Link]")
List<DepartmentDTO> findAllDepartmentsWithEmployeeCount();

Q23. How do you implement database auditing


in Spring Data JPA?
Answer:

Spring Data JPA provides automatic auditing of entities:

Enable Auditing:
@Configuration
@EnableJpaAuditing(auditorAwareRef = "auditorProvider")
public class JpaConfig {

@Bean
public AuditorAware<String> auditorProvider() {
return new AuditorAwareImpl();
}

public class AuditorAwareImpl implements AuditorAware<String> {

@Override
public Optional<String> getCurrentAuditor() {
Authentication authentication = SecurityContextHolder
.getContext().getAuthentication();

if (authentication == null || ![Link]() ||


authentication instanceof AnonymousAuthenticationToken) {
return [Link]();
}

return [Link]([Link]());
}

Auditable Base Entity:


@MappedSuperclass
@EntityListeners([Link])
public abstract class Auditable<U> {

@CreatedBy
@Column(updatable = false)
protected U createdBy;

@CreatedDate
@Column(updatable = false)
protected LocalDateTime createdDate;

@LastModifiedBy
protected U lastModifiedBy;

@LastModifiedDate
protected LocalDateTime lastModifiedDate;

@Version
protected Long version;

Entity Usage:
@Entity
@Table(name = "users")
public class User extends Auditable<String> {

@Id
@GeneratedValue(strategy = [Link])
private Long id;

private String username;


private String email;

// getters and setters

Q24. Explain optimistic vs pessimistic locking


in JPA.
Answer:
Optimistic Locking (@Version):
@Entity
public class Product {
@Id
private Long id;

private String name;


private BigDecimal price;

@Version
private Long version;

// getters and setters

@Service
public class ProductService {

@Transactional
public Product updateProductPrice(Long id, BigDecimal newPrice) {
try {
Product product = [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("Product not
found"));
[Link](newPrice);
return [Link](product);
} catch (OptimisticLockException e) {
throw new ConcurrentModificationException(
"Product was updated by another transaction");
}
}

Pessimistic Locking:
public interface ProductRepository extends JpaRepository<Product, Long> {

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT p FROM Product p WHERE [Link] = :id")
Optional<Product> findByIdWithWriteLock(@Param("id") Long id);

@Lock(LockModeType.PESSIMISTIC_READ)
@Query("SELECT p FROM Product p WHERE [Link] = :id")
Optional<Product> findByIdWithReadLock(@Param("id") Long id);

@Service
public class InventoryService {
@Transactional
public void decrementStock(Long productId, int quantity) {
// Acquires database lock
Product product = productRepository
.findByIdWithWriteLock(productId)
.orElseThrow(() -> new ResourceNotFoundException("Product not
found"));

if ([Link]() < quantity) {


throw new InsufficientStockException("Not enough stock");
}

[Link]([Link]() - quantity);
[Link](product);
// Lock released when transaction completes
}

Comparison:

Aspect Optimistic Locking Pessimistic Locking

Low contention High contention


When Used
scenarios scenarios
Mechanism Version field check Database row lock
Performance Better (no locks) Slower (lock overhead)
Concurrency Higher Lower
Fails at commit Blocks until lock
Failure Mode
available

Table 4: Optimistic vs Pessimistic Locking

8. Transaction Management
Q25. Explain Spring's transaction propagation
levels with examples.
Answer:

Transaction propagation defines how transactions relate to each other:

Propagation Behavior
Join existing transaction or create
REQUIRED (default)
new one
Always create new transaction,
REQUIRES_NEW
suspend current
Must run within existing transaction,
MANDATORY
else throw exception
Execute within nested transaction if
NESTED
current exists
Join transaction if exists, else run
SUPPORTS
non-transactional
Execute non-transactionally, suspend
NOT_SUPPORTED
current transaction
Execute non-transactionally, throw
NEVER
exception if transaction exists

Table 5: Transaction Propagation Levels


Examples:

@Service
public class OrderService {

@Autowired
private PaymentService paymentService;
@Autowired
private NotificationService notificationService;

@Transactional(propagation = [Link])
public void createOrder(OrderRequest request) {
// Transaction T1 starts here
Order order = [Link](new Order(request));

// Joins T1 (REQUIRED)
[Link](order);

// Creates new T2, independent of T1 (REQUIRES_NEW)


[Link](order);

// If exception here, T1 rolls back but T2 already committed


}

@Service
public class PaymentService {
@Transactional(propagation = [Link])
public void processPayment(Order order) {
// Joins existing transaction from OrderService
Payment payment = new Payment(order);
[Link](payment);

// If exception here, entire OrderService transaction rolls back


}

@Service
public class NotificationService {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void sendConfirmation(Order order) {
// Creates new independent transaction
// Suspends OrderService transaction temporarily
Notification notification = new Notification(order);
[Link](notification);
[Link](notification);

// Commits independently, even if OrderService transaction fails


}

@Service
public class AuditService {

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logActivity(String action, String details) {
// Always create new transaction for audit log
// Ensures audit is saved even if parent transaction fails
AuditLog log = new AuditLog(action, details, [Link]());
[Link](log);
}

@Service
public class ReportService {

@Transactional(propagation = [Link], readOnly = true)


public List<Report> generateReport() {
// Will join transaction if exists, otherwise runs without transaction
// Optimized for read-only operations
return [Link]();
}
}

Q26. How do you handle distributed


transactions in microservices?
Answer:

Distributed transactions require different approaches than traditional ACID


transactions:

1. Two-Phase Commit (2PC) - Avoid in Microservices:

 Blocking protocol
 Poor performance and scalability
 Single point of failure
2. Saga Pattern (Recommended):
See Q18 for detailed implementation.

3. Event Sourcing:
@Service
public class OrderEventSourceService {

@Autowired
private EventStore eventStore;

public Order createOrder(OrderRequest request) {


OrderCreatedEvent event = new OrderCreatedEvent(
[Link](),
[Link](),
[Link](),
[Link]()
);

[Link](event);
publishEvent(event);

return buildOrderFromEvents([Link]());
}

private Order buildOrderFromEvents(UUID orderId) {


List<DomainEvent> events = [Link](orderId);
Order order = new Order();

[Link](event -> {
if (event instanceof OrderCreatedEvent) {
[Link]((OrderCreatedEvent) event);
} else if (event instanceof PaymentProcessedEvent) {
[Link]((PaymentProcessedEvent) event);
}
// ... handle other events
});

return order;
}

4. Eventual Consistency with Outbox Pattern:


@Service
public class OrderService {

@Autowired
private OrderRepository orderRepository;
@Autowired
private OutboxRepository outboxRepository;

@Transactional
public Order createOrder(OrderRequest request) {
// Single local transaction
Order order = [Link](new Order(request));

// Store event in outbox table (same transaction)


OutboxEvent event = [Link]()
.aggregateId([Link]().toString())
.eventType("OrderCreated")
.payload(serializeOrder(order))
.createdAt([Link]())
.published(false)
.build();

[Link](event);

return order;
}

// Separate process publishes events


@Component
public class OutboxPublisher {

@Scheduled(fixedDelay = 1000)
@Transactional
public void publishPendingEvents() {
List<OutboxEvent> events = outboxRepository
.findByPublishedFalseOrderByCreatedAtAsc(
[Link](0, 100));

[Link](event -> {
try {
[Link]([Link](), [Link]());
[Link](true);
[Link]([Link]());
[Link](event);
} catch (Exception e) {
[Link]("Failed to publish event: {}", [Link](), e);
[Link]([Link]() + 1);
[Link](event);
}
});
}

9. Performance Optimization
Q27. How do you optimize Spring Boot
application performance?
Answer:

Multiple strategies for performance optimization[14]:

1. Connection Pooling (HikariCP):


spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariCP

2. Caching:
@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager(
"users", "products", "categories");
[Link]([Link]()
.expireAfterWrite(10, [Link])
.maximumSize(1000));
return cacheManager;
}
}

@Service
public class UserService {

@Cacheable(value = "users", key = "#id")


public User getUserById(Long id) {
return [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
}

@CacheEvict(value = "users", key = "#[Link]")


public User updateUser(User user) {
return [Link](user);
}

@Caching(evict = {
@CacheEvict(value = "users", allEntries = true),
@CacheEvict(value = "userStats", allEntries = true)
})
public void refreshCache() {
// Evicts multiple caches
}

3. Async Processing:
@Configuration
@EnableAsync
public class AsyncConfig {

@Bean(name = "taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
[Link](5);
[Link](10);
[Link](100);
[Link]("async-");
[Link]();
return executor;
}

@Service
public class NotificationService {

@Async("taskExecutor")
public CompletableFuture<Void> sendEmailAsync(String to, String subject) {
// Long-running operation
[Link](to, subject, body);
return [Link](null);
}

@Async
public void processLargeDataset(List<Data> dataset) {
// Process in background
[Link](this::process);
}

4. Database Optimization:
// Use projections instead of full entities
public interface UserProjection {
Long getId();
String getUsername();
String getEmail();
}

public interface UserRepository extends JpaRepository<User, Long> {

List<UserProjection> findAllProjectedBy();

@Query(value = "SELECT u FROM User u WHERE [Link] = :status",


countQuery = "SELECT COUNT(u) FROM User u WHERE [Link]
= :status")
Page<User> findByStatus(@Param("status") String status, Pageable
pageable);

5. Lazy Initialization:
spring:
main:
lazy-initialization: true # Use selectively

6. HTTP/2 Support:
server:
http2:
enabled: true
compression:
enabled: true
mime-types: application/json,application/xml,text/html,text/xml,text/plain
min-response-size: 1024

Q28. How do you implement caching


strategies in Spring Boot?
Answer:

Different caching strategies for various scenarios:


1. Cache-Aside (Lazy Loading):
@Service
public class ProductService {

@Cacheable(value = "products", key = "#id")


public Product getProduct(Long id) {
// Cache miss: Load from database
return [Link](id)
.orElseThrow(() -> new ResourceNotFoundException("Product not
found"));
}

2. Write-Through:
@Service
public class ProductService {

@CachePut(value = "products", key = "#[Link]")


public Product updateProduct(Product product) {
// Update database and cache simultaneously
return [Link](product);
}

3. Write-Behind:
@Service
public class ProductService {

@Autowired
private CacheManager cacheManager;

@Async
public void updateProductAsync(Product product) {
// Update cache immediately
Cache cache = [Link]("products");
[Link]([Link](), product);

// Persist to database asynchronously


[Link](() ->
[Link](product));
}

4. Refresh-Ahead:
@Component
public class CacheRefresher {
@Scheduled(fixedDelay = 300000) // 5 minutes
public void refreshPopularProducts() {
List<Long> popularProductIds = analyticsService
.getPopularProductIds();

[Link](id ->
[Link](id)); // Warms cache
}

5. Multi-Level Caching:
@Configuration
public class CacheConfig {

@Bean
@Primary
public CacheManager compositeCacheManager() {
CompositeCacheManager cacheManager = new
CompositeCacheManager();

List<CacheManager> managers = [Link](


caffeineLocalCache(),
redisCacheManager()
);

[Link](managers);
[Link](false);

return cacheManager;
}

@Bean
public CacheManager caffeineLocalCache() {
// L1 cache: Fast, local memory
CaffeineCacheManager manager = new CaffeineCacheManager();
[Link]([Link]()
.expireAfterWrite(5, [Link])
.maximumSize(1000));
return manager;
}

@Bean
public CacheManager redisCacheManager() {
// L2 cache: Distributed, shared across instances
RedisCacheConfiguration config = RedisCacheConfiguration
.defaultCacheConfig()
.entryTtl([Link](30));

return [Link](redisConnectionFactory())
.cacheDefaults(config)
.build();
}

10. Messaging and Event-Driven


Architecture
Q29. How do you implement Kafka integration
in Spring Boot?
Answer:

Spring Kafka provides integration with Apache Kafka:

Configuration:
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: order-service
auto-offset-reset: earliest
key-deserializer: [Link]
value-deserializer:
[Link]
properties:
[Link]: [Link]
producer:
key-serializer: [Link]
value-serializer: [Link]
acks: all
retries: 3

Producer:
@Service
@Slf4j
public class OrderEventProducer {

@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;

public void publishOrderCreated(Order order) {


OrderEvent event = [Link]()
.orderId([Link]())
.userId([Link]())
.amount([Link]())
.timestamp([Link]())
.build();

CompletableFuture<SendResult<String, OrderEvent>> future =


[Link]("order-created",
[Link]().toString(),
event);

[Link]((result, ex) -> {


if (ex != null) {
[Link]("Failed to send message: {}", [Link]());
} else {
[Link]("Message sent successfully: {}",
[Link]());
}
});
}

Consumer:
@Component
@Slf4j
public class OrderEventConsumer {

@Autowired
private NotificationService notificationService;

@KafkaListener(
topics = "order-created",
groupId = "notification-service",
containerFactory = "kafkaListenerContainerFactory"
)
public void handleOrderCreated(
@Payload OrderEvent event,
@Header(KafkaHeaders.RECEIVED_PARTITION) int partition,
@Header([Link]) long offset) {

[Link]("Received order event: {}, partition: {}, offset: {}",


[Link](), partition, offset);

try {
[Link](event);
} catch (Exception e) {
[Link]("Error processing order event: {}", [Link]());
throw e; // Triggers retry or DLQ
}
}

@KafkaListener(topics = "order-created")
public void handleOrderCreatedBatch(
List<OrderEvent> events,
Acknowledgment acknowledgment) {

[Link]("Received batch of {} events", [Link]());

[Link](event -> {
try {
processEvent(event);
} catch (Exception e) {
[Link]("Failed to process event: {}", event, e);
}
});

[Link]();
}

Error Handling with DLQ:


@Configuration
public class KafkaConfig {

@Bean
public ConcurrentKafkaListenerContainerFactory<String, OrderEvent>
kafkaListenerContainerFactory() {

ConcurrentKafkaListenerContainerFactory<String, OrderEvent> factory =


new ConcurrentKafkaListenerContainerFactory<>();
[Link](consumerFactory());
[Link](errorHandler());

return factory;
}

@Bean
public DefaultErrorHandler errorHandler() {
// Retry with exponential backoff
BackOff backOff = new ExponentialBackOffWithMaxRetries(3);
((ExponentialBackOffWithMaxRetries) backOff).setInitialInterval(1000L);
((ExponentialBackOffWithMaxRetries) backOff).setMultiplier(2.0);
((ExponentialBackOffWithMaxRetries) backOff).setMaxInterval(10000L);

DefaultErrorHandler errorHandler = new DefaultErrorHandler(


new DeadLetterPublishingRecoverer(kafkaTemplate(),
(record, ex) -> new TopicPartition(
[Link]() + ".DLT",
[Link]()
)
),
backOff
);

// Don't retry on specific exceptions


[Link](
[Link],
[Link]
);

return errorHandler;
}

Q30. Explain idempotent consumer pattern in


event-driven systems.
Answer:

Idempotent consumers handle duplicate messages gracefully:

@Service
@Transactional
public class PaymentService {

@Autowired
private PaymentRepository paymentRepository;
@Autowired
private ProcessedEventRepository eventRepository;

@KafkaListener(topics = "payment-requested")
public void processPayment(@Payload PaymentRequestEvent event) {
String eventId = [Link]();

// Check if already processed (idempotency key)


if ([Link](eventId)) {
[Link]("Event {} already processed, skipping", eventId);
return;
}

try {
// Process payment
Payment payment = [Link]()
.orderId([Link]())
.amount([Link]())
.status([Link])
.build();

[Link](payment);
// Mark as processed
ProcessedEvent processedEvent = [Link]()
.eventId(eventId)
.eventType("PaymentRequested")
.processedAt([Link]())
.build();

[Link](processedEvent);

[Link]("Payment processed successfully for event: {}", eventId);

} catch (Exception e) {
[Link]("Failed to process payment for event: {}", eventId, e);
throw e;
}
}

@Entity
@Table(
name = "processed_events",
indexes = @Index(name = "idx_event_id", columnList = "event_id")
)
public class ProcessedEvent {
@Id
@GeneratedValue(strategy = [Link])
private Long id;

@Column(name = "event_id", unique = true, nullable = false)


private String eventId;

private String eventType;


private LocalDateTime processedAt;

Alternative: Use Database Constraints:


@Entity
@Table(name = "payments")
public class Payment {
@Id
@GeneratedValue(strategy = [Link])
private Long id;

@Column(name = "order_id", unique = true, nullable = false)


private Long orderId; // Unique constraint prevents duplicates

private BigDecimal amount;


private PaymentStatus status;
}

@Service
public class PaymentService {

@KafkaListener(topics = "payment-requested")
public void processPayment(@Payload PaymentRequestEvent event) {
try {
Payment payment = [Link]()
.orderId([Link]())
.amount([Link]())
.status([Link])
.build();

[Link](payment);
} catch (DataIntegrityViolationException e) {
// Duplicate detected, already processed
[Link]("Payment for order {} already exists", [Link]());
}
}

11. Monitoring, Observability, and


Actuator
Q31. How do you implement monitoring and
observability in Spring Boot?
Answer:

Spring Boot Actuator provides production-ready features[11]:

Configuration:
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus,env,beans,mappings
endpoint:
health:
show-details: always
probes:
enabled: true
metrics:
export:
prometheus:
enabled: true
distribution:
percentiles-histogram:
[Link]: true
tracing:
sampling:
probability: 1.0

Custom Health Indicator:


@Component
public class CustomHealthIndicator implements HealthIndicator {

@Autowired
private ExternalService externalService;

@Override
public Health health() {
try {
boolean isHealthy = [Link]();

if (isHealthy) {
return [Link]()
.withDetail("external-service", "available")
.withDetail("response-time", "45ms")
.build();
} else {
return [Link]()
.withDetail("external-service", "unavailable")
.withDetail("error", "Connection timeout")
.build();
}
} catch (Exception e) {
return [Link]()
.withDetail("external-service", "error")
.withException(e)
.build();
}
}

Custom Metrics:
@Service
public class OrderService {

private final Counter orderCounter;


private final Timer orderProcessingTimer;

public OrderService(MeterRegistry meterRegistry) {


[Link] = [Link]("[Link]")
.description("Total number of orders created")
.tag("type", "order")
.register(meterRegistry);

[Link] = [Link]("[Link]")
.description("Time taken to process an order")
.register(meterRegistry);
}

public Order createOrder(OrderRequest request) {


return [Link](() -> {
Order order = processOrder(request);
[Link]();
return order;
});
}

Micrometer with Prometheus:


@Configuration
public class MetricsConfig {

@Bean
public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> [Link]()
.commonTags("application", "order-service")
.commonTags("environment", "production");
}

Distributed Tracing (see Q17 for details)

Q32. How do you implement custom Actuator


endpoints?
Answer:

Create custom endpoints for application-specific monitoring:

@Component
@Endpoint(id = "custom")
public class CustomEndpoint {

@ReadOperation
public Map<String, Object> customInfo() {
Map<String, Object> info = new HashMap<>();
[Link]("application", "order-service");
[Link]("version", "1.0.0");
[Link]("uptime", getUptime());
[Link]("activeOrders", getActiveOrderCount());
return info;
}

@ReadOperation
public String getStatistics(@Selector String name) {
if ("orders".equals(name)) {
return getOrderStatistics();
} else if ("users".equals(name)) {
return getUserStatistics();
}
return "Unknown statistic: " + name;
}

@WriteOperation
public void refreshCache() {
[Link]()
.forEach(name -> [Link](name).clear());
}

@DeleteOperation
public void clearMetrics(@Selector String metricName) {

[Link]([Link](metricName).meters().get(0).getId(
));
}

Web-specific Endpoint:
@Component
@WebEndpoint(id = "orders")
public class OrdersEndpoint {

@Autowired
private OrderService orderService;

@ReadOperation
public ResponseEntity<List<Order>> getRecentOrders(
@Nullable @Selector String status) {

List<Order> orders;
if (status != null) {
orders = [Link](status);
} else {
orders = [Link](10);
}

return [Link](orders);
}
}

12. Testing Strategies


Q33. How do you implement comprehensive
testing in Spring Boot?
Answer:

Layered testing approach with different test types:

1. Unit Tests (with Mockito):


@ExtendWith([Link])
class UserServiceTest {

@Mock
private UserRepository userRepository;

@Mock
private PasswordEncoder passwordEncoder;

@InjectMocks
private UserService userService;

@Test
void shouldCreateUser() {
// Given
UserRequest request = new UserRequest("john", "password");
User user = new User(1L, "john", "encoded");

when([Link]("password")).thenReturn("encoded");
when([Link](any([Link]))).thenReturn(user);

// When
User result = [Link](request);

// Then
assertThat([Link]()).isEqualTo("john");
verify(userRepository).save(any([Link]));
verify(passwordEncoder).encode("password");
}

2. Integration Tests (@SpringBootTest):


@SpringBootTest
@AutoConfigureMockMvc
@Transactional
class UserControllerIntegrationTest {

@Autowired
private MockMvc mockMvc;

@Autowired
private ObjectMapper objectMapper;

@Autowired
private UserRepository userRepository;

@Test
void shouldCreateUser() throws Exception {
UserRequest request = new UserRequest("john", "password");

[Link](post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content([Link](request)))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.username").value("john"))
.andExpect(jsonPath("$.id").exists());

assertThat([Link]("john")).isPresent();
}

3. Repository Tests (@DataJpaTest):


@DataJpaTest
@AutoConfigureTestDatabase(replace =
[Link])
class UserRepositoryTest {

@Autowired
private UserRepository userRepository;

@Autowired
private TestEntityManager entityManager;

@Test
void shouldFindUserByUsername() {
// Given
User user = new User("john", "password");
[Link](user);
[Link]();

// When
Optional<User> found = [Link]("john");

// Then
assertThat(found).isPresent();
assertThat([Link]().getUsername()).isEqualTo("john");
}

@Test
void shouldFindUsersByStatus() {
// Given
User active1 = new User("user1", "pass1", [Link]);
User active2 = new User("user2", "pass2", [Link]);
User inactive = new User("user3", "pass3", [Link]);

[Link](active1);
[Link](active2);
[Link](inactive);
[Link]();

// When
List<User> activeUsers = [Link]([Link]);

// Then
assertThat(activeUsers).hasSize(2);
assertThat(activeUsers).extracting(User::getStatus)
.containsOnly([Link]);
}

4. Slice Tests (@WebMvcTest):


@WebMvcTest([Link])
class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

@Autowired
private ObjectMapper objectMapper;

@Test
@WithMockUser(username = "admin", roles = "ADMIN")
void shouldGetUserById() throws Exception {
User user = new User(1L, "john", "john@[Link]");

when([Link](1L)).thenReturn(user);

[Link](get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value(1))
.andExpect(jsonPath("$.username").value("john"));
}

5. TestContainers for Integration Tests:


@SpringBootTest
@Testcontainers
@AutoConfigureMockMvc
class OrderServiceIntegrationTest {

@Container
static PostgreSQLContainer<?> postgres = new
PostgreSQLContainer<>("postgres:15")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");

@Container
static KafkaContainer kafka = new KafkaContainer(
[Link]("confluentinc/cp-kafka:7.4.0"));

@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
[Link]("[Link]", postgres::getJdbcUrl);
[Link]("[Link]", postgres::getUsername);
[Link]("[Link]", postgres::getPassword);
[Link]("[Link]-servers", kafka::getBootstrapServers);
}

@Autowired
private OrderService orderService;

@Test
void shouldCreateOrderWithRealDatabaseAndKafka() {
OrderRequest request = new OrderRequest(/* ... */);
Order order = [Link](request);

assertThat([Link]()).isNotNull();
assertThat([Link]()).isEqualTo([Link]);
}

13. Deployment and DevOps


Q34. How do you implement Spring Boot
application deployment strategies?
Answer:

Multiple deployment approaches for different scenarios:

1. Containerized Deployment (Docker):

Dockerfile (Multi-Stage Build):

Build stage
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY [Link] .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests

Runtime stage
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar [Link]

Non-root user for security


RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring

EXPOSE 8080

ENV JAVA_OPTS="-Xmx512m -Xms256m"


ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar [Link]"]

[Link]:
version: '3.8'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
- SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/myapp
- SPRING_REDIS_HOST=redis
depends_on:
- db
- redis
networks:
- app-network
restart: unless-stopped

db:
image: postgres:15
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: password
volumes:
- postgres-data:/var/lib/postgresql/data
networks:
- app-network

redis:
image: redis:7-alpine
networks:
- app-network

volumes:
postgres-data:

networks:
app-network:
driver: bridge

2. Kubernetes Deployment:

[Link]:
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
labels:
app: order-service
spec:
replicas: 3
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: myregistry/order-
service:1.0.0
ports:
- containerPort: 8080
env:
- name: SPRING_PROFILES_ACTIVE
value: "prod"
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef:
name: db-secrets
key: url
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 20
periodSeconds: 5
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:

 protocol: TCP
port: 80
targetPort: 8080
type: LoadBalancer
3. CI/CD Pipeline (GitHub Actions):

name: CI/CD Pipeline

on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3

- name: Set up JDK 17


uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Cache Maven packages


uses: actions/cache@v3
with:
path: ~/.m2
key: ${{ [Link] }}-m2-${{ hashFiles('**/[Link]') }}

- name: Run tests


run: mvn clean test

- name: Generate coverage report


run: mvn jacoco:report

- name: Upload coverage to Codecov


uses: codecov/codecov-action@v3

build:
needs: test
runs-on: ubuntu-latest
if: [Link] == 'refs/heads/main'

steps:
- uses: actions/checkout@v3

- name: Set up JDK 17


uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'

- name: Build with Maven


run: mvn clean package -DskipTests

- name: Build Docker image


run: docker build -t myregistry/order-service:${{ [Link] }} .

- name: Login to Docker Registry


run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u $
{{ secrets.DOCKER_USERNAME }} --password-stdin

- name: Push Docker image


run: docker push myregistry/order-service:${{ [Link] }}

- name: Deploy to Kubernetes


uses: azure/k8s-deploy@v4
with:
manifests: |
k8s/[Link]
k8s/[Link]
images: myregistry/order-service:${{ [Link] }}

Q35. How do you implement blue-green and


canary deployments?
Answer:

Blue-Green Deployment:

Blue deployment (current)


apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-blue
spec:
replicas: 3
selector:
matchLabels:
app: order-service
version: blue
template:
metadata:
labels:
app: order-service
version: blue
spec:
containers:
- name: order-service
image: myregistry/order-service:1.0.0

Green deployment (new)


apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-green
spec:
replicas: 3
selector:
matchLabels:
app: order-service
version: green
template:
metadata:
labels:
app: order-service
version: green
spec:
containers:
- name: order-service
image: myregistry/order-service:2.0.0

Service switches between


blue and green
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
version: blue # Change to 'green' to switch traffic
ports:

 protocol: TCP
port: 80
targetPort: 8080
Canary Deployment:
Stable version (90% traffic)
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-stable
spec:
replicas: 9
selector:
matchLabels:
app: order-service
track: stable
template:
metadata:
labels:
app: order-service
track: stable
version: "1.0.0"
spec:
containers:
- name: order-service
image: myregistry/order-service:1.0.0

Canary version (10%


traffic)
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service-canary
spec:
replicas: 1
selector:
matchLabels:
app: order-service
track: canary
template:
metadata:
labels:
app: order-service
track: canary
version: "2.0.0"
spec:
containers:
- name: order-service
image: myregistry/order-service:2.0.0

apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service # Matches both stable and canary
ports:

 protocol: TCP
port: 80
targetPort: 8080

14. Design Patterns and Best


Practices
Q36. Explain common design patterns used in
Spring Boot applications.
Answer:

1. Repository Pattern (Data Access):


public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
List<User> findByStatus(UserStatus status);
}

@Service
public class UserService {
@Autowired
private UserRepository userRepository;

// Business logic uses repository abstraction

2. Service Layer Pattern:


@Service
@Transactional
public class OrderService {

@Autowired
private OrderRepository orderRepository;
@Autowired
private PaymentService paymentService;
@Autowired
private InventoryService inventoryService;

public Order createOrder(OrderRequest request) {


// Orchestrate business logic
validateOrder(request);
Order order = [Link](new Order(request));
[Link](order);
[Link]([Link]());
return order;
}

3. Factory Pattern:
public interface PaymentProcessor {
Payment process(PaymentRequest request);
}

@Component
public class CreditCardProcessor implements PaymentProcessor {
public Payment process(PaymentRequest request) {
// Credit card logic
}
}

@Component
public class PayPalProcessor implements PaymentProcessor {
public Payment process(PaymentRequest request) {
// PayPal logic
}
}

@Component
public class PaymentProcessorFactory {

@Autowired
private Map<String, PaymentProcessor> processors;

public PaymentProcessor getProcessor(PaymentMethod method) {


return switch (method) {
case CREDIT_CARD -> [Link]("creditCardProcessor");
case PAYPAL -> [Link]("payPalProcessor");
default -> throw new UnsupportedOperationException(
"Payment method not supported");
};
}

}
4. Strategy Pattern:
public interface PricingStrategy {
BigDecimal calculatePrice(Order order);
}

@Component("regularPricing")
public class RegularPricingStrategy implements PricingStrategy {
public BigDecimal calculatePrice(Order order) {
return [Link]().stream()
.map(Item::getPrice)
.reduce([Link], BigDecimal::add);
}
}

@Component("premiumPricing")
public class PremiumPricingStrategy implements PricingStrategy {
public BigDecimal calculatePrice(Order order) {
BigDecimal total = [Link]().stream()
.map(Item::getPrice)
.reduce([Link], BigDecimal::add);
return [Link](new BigDecimal("0.9")); // 10% discount
}
}

@Service
public class OrderService {

@Autowired
@Qualifier("regularPricing")
private PricingStrategy pricingStrategy;

public BigDecimal calculateOrderPrice(Order order, UserTier tier) {


PricingStrategy strategy = tier == [Link]
? premiumPricingStrategy
: regularPricingStrategy;
return [Link](order);
}

5. Builder Pattern:
@Builder
@Data
public class OrderRequest {
private Long userId;
private List<OrderItem> items;
private Address shippingAddress;
private PaymentMethod paymentMethod;

public static class OrderRequestBuilder {


public OrderRequestBuilder defaultShipping() {
[Link] = [Link]();
return this;
}
}

// Usage
OrderRequest request = [Link]()
.userId(1L)
.items(items)
.defaultShipping()
.paymentMethod(PaymentMethod.CREDIT_CARD)
.build();

6. Observer Pattern (Event Publishing):


public class OrderCreatedEvent extends ApplicationEvent {
private final Order order;

public OrderCreatedEvent(Object source, Order order) {


super(source);
[Link] = order;
}

public Order getOrder() {


return order;
}

@Service
public class OrderService {

@Autowired
private ApplicationEventPublisher eventPublisher;

public Order createOrder(OrderRequest request) {


Order order = [Link](new Order(request));
[Link](new OrderCreatedEvent(this, order));
return order;
}

@Component
public class OrderEventListener {

@EventListener
@Async
public void handleOrderCreated(OrderCreatedEvent event) {
Order order = [Link]();
// Send notification, update analytics, etc.
}

15. Scenario-Based Questions


Q37. How would you design a high-throughput
order processing system?
Answer:

Architecture Design:

1. API Gateway: Rate limiting, authentication, request routing


2. Load Balancer: Distribute traffic across multiple instances
3. Order Service: Stateless, horizontally scalable
4. Message Queue (Kafka): Asynchronous order processing
5. Database: Read replicas, connection pooling, query optimization
6. Cache Layer (Redis): Product catalog, user sessions, frequently
accessed data
7. CDN: Static content delivery
Implementation:

@RestController
@RequestMapping("/api/orders")
public class OrderController {

@Autowired
private OrderService orderService;

@PostMapping
@ResponseStatus([Link])
public OrderResponse createOrder(@RequestBody @Valid OrderRequest
request) {
// Synchronous validation only
String orderId = [Link](request);

// Return immediately without waiting for processing


return [Link]()
.orderId(orderId)
.status("PENDING")
.message("Order submitted for processing")
.build();
}
@GetMapping("/{orderId}")
@Cacheable(value = "orders", key = "#orderId")
public OrderDetails getOrderStatus(@PathVariable String orderId) {
return [Link](orderId);
}

@Service
public class OrderService {

@Autowired
private KafkaTemplate<String, OrderEvent> kafkaTemplate;
@Autowired
private RedisTemplate<String, OrderStatus> redisTemplate;

public String submitOrder(OrderRequest request) {


String orderId = [Link]().toString();

// Store initial status in Redis


[Link]().set(
"order:" + orderId,
[Link],
[Link](24)
);

// Publish to Kafka for asynchronous processing


OrderEvent event = [Link]()
.orderId(orderId)
.userId([Link]())
.items([Link]())
.timestamp([Link]())
.build();

[Link]("order-submitted", orderId, event);

return orderId;
}

@Component
public class OrderProcessor {

@KafkaListener(
topics = "order-submitted",
concurrency = "10" // Process 10 messages in parallel
)
@Transactional
public void processOrder(@Payload OrderEvent event) {
String orderId = [Link]();

try {
// Process order with all business logic
validateInventory(event);
processPayment(event);
reserveItems(event);

updateOrderStatus(orderId, [Link]);

} catch (PaymentException e) {
updateOrderStatus(orderId, OrderStatus.PAYMENT_FAILED);
publishToDeadLetterQueue(event, e);
} catch (Exception e) {
updateOrderStatus(orderId, [Link]);
throw e; // Trigger retry
}
}

private void updateOrderStatus(String orderId, OrderStatus status) {


[Link]().set("order:" + orderId, status);
// Also update database asynchronously
[Link](orderId, status);
}

Performance Optimizations:

• Connection pooling (HikariCP with optimal settings)


• Database query optimization (indexes, avoid N+1)
• Caching (Redis for hot data)
• Async processing (Kafka for decoupling)
• Batch processing where possible
• Circuit breakers for external dependencies
• Database sharding for horizontal scaling
• Read replicas for read-heavy operations

Q38. How would you troubleshoot a


production memory leak?
Answer:

Step-by-step troubleshooting approach:

1. Detect Memory Leak:


Enable monitoring
management:
endpoints:
web:
exposure:
include: metrics,heapdump,threaddump
metrics:
export:
prometheus:
enabled: true

2. Generate Heap Dump:

Via Actuator
curl -X GET [Link] -o [Link]

Via JVM
jmap -dump:live,format=b,file=[Link] <PID>

3. Analyze with MAT (Memory Analyzer Tool):

 Identify dominator tree


 Find leak suspects
 Analyze object retention paths
4. Common Spring Boot Memory Leak Causes:

// Problem: Not closing resources


@Service
public class BadService {
public void processFile(String path) {
InputStream is = new FileInputStream(path);
// Missing close() - resource leak
}
}

// Solution: Use try-with-resources


@Service
public class GoodService {
public void processFile(String path) {
try (InputStream is = new FileInputStream(path)) {
// Auto-closed
} catch (IOException e) {
// Handle
}
}
}

// Problem: Unbounded cache


@Configuration
public class BadCacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
// No size limit - memory leak
return manager;
}
}

// Solution: Bounded cache


@Configuration
public class GoodCacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
[Link]([Link]()
.maximumSize(10000)
.expireAfterWrite(10, [Link]));
return manager;
}
}

// Problem: Thread pool not shutdown


@Service
public class BadAsyncService {
private ExecutorService executor = [Link](10);
// Never shutdown - thread leak
}

// Solution: Use Spring's managed executor


@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(destroyMethod = "shutdown")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
[Link](5);
[Link](10);
[Link]();
return executor;
}
}

5. JVM Tuning:

Set appropriate heap size


-Xms2g -Xmx2g

Enable GC logging
-Xlog:gc*:file=[Link]:time,uptime,level,tags

Use appropriate GC
-XX:+UseG1GC

Set max direct memory


-XX:MaxDirectMemorySize=512m

Q39. How would you implement a rate limiting


strategy?
Answer:

Multiple approaches for rate limiting:

1. Token Bucket using Bucket4j:

@Configuration
public class RateLimitConfig {

@Bean
public Bucket bucket() {
Bandwidth limit = [Link](100, [Link](100,
[Link](1)));
return [Link]()
.addLimit(limit)
.build();
}

@Component
public class RateLimitInterceptor implements HandlerInterceptor {

@Autowired
private CacheManager cacheManager;

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {

String clientId = getClientId(request);


Bucket bucket = getBucketForClient(clientId);

if ([Link](1)) {
return true;
} else {
[Link](HttpStatus.TOO_MANY_REQUESTS.value());
[Link]().write("Rate limit exceeded");
return false;
}
}

private Bucket getBucketForClient(String clientId) {


Cache cache = [Link]("rate-limits");
Bucket bucket = [Link](clientId, [Link]);

if (bucket == null) {
Bandwidth limit = [Link](
100,
[Link](100, [Link](1))
);
bucket = [Link]().addLimit(limit).build();
[Link](clientId, bucket);
}

return bucket;
}

2. Redis-based Rate Limiting:

@Component
public class RedisRateLimiter {

@Autowired
private StringRedisTemplate redisTemplate;

public boolean isAllowed(String key, int maxRequests, Duration window) {


String redisKey = "rate_limit:" + key;
long currentTime = [Link]();
long windowStart = currentTime - [Link]();

// Remove old entries


[Link]().removeRangeByScore(
redisKey, 0, windowStart);

// Count current requests


Long count = [Link]().zCard(redisKey);
if (count != null && count < maxRequests) {
// Add new request
[Link]().add(
redisKey,
[Link](currentTime),
currentTime
);
[Link](redisKey, window);
return true;
}

return false;
}

@RestController
public class ApiController {

@Autowired
private RedisRateLimiter rateLimiter;

@GetMapping("/api/resource")
public ResponseEntity<?> getResource(HttpServletRequest request) {
String clientId = getClientId(request);

if (![Link](clientId, 100, [Link](1))) {


return [Link](HttpStatus.TOO_MANY_REQUESTS)
.body("Rate limit exceeded");
}

return [Link]([Link]());
}

3. API Gateway Rate Limiting (Spring Cloud Gateway):

spring:
cloud:
gateway:
routes:
- id: order_service
uri: [Link]
predicates:
- Path=/api/orders/**
filters:
- name: RequestRateLimiter
args:
[Link]: 10
[Link]: 20
[Link]: 1
key-resolver: "#{@userKeyResolver}"

@Configuration
public class GatewayConfig {

@Bean
public KeyResolver userKeyResolver() {
return exchange -> {
String userId = [Link]()
.getHeaders()
.getFirst("X-User-Id");
return [Link](userId != null ? userId : "anonymous");
};
}

Q40. How would you implement a multi-tenant


Spring Boot application?
Answer:

Database-per-Tenant Approach:

@Configuration
public class MultiTenantConfig {

@Bean
public DataSource dataSource() {
return new TenantRoutingDataSource();
}

public class TenantRoutingDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
return [Link]();
}

public class TenantContext {

private static final ThreadLocal<String> CURRENT_TENANT = new


ThreadLocal<>();
public static void setCurrentTenant(String tenantId) {
CURRENT_TENANT.set(tenantId);
}

public static String getCurrentTenant() {


return CURRENT_TENANT.get();
}

public static void clear() {


CURRENT_TENANT.remove();
}

@Component
public class TenantInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String tenantId = [Link]("X-Tenant-ID");

if (tenantId != null) {
[Link](tenantId);
} else {
[Link](HttpStatus.BAD_REQUEST.value());
return false;
}

return true;
}

@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) {
[Link]();
}

Schema-per-Tenant Approach:

@Entity
@Table(name = "users")
@[Link]
public class User {
@Id
private Long id;
// Hibernate automatically filters by tenant
private String username;

@Configuration
public class HibernateConfig {

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean em =
new LocalContainerEntityManagerFactoryBean();

// Configure multi-tenancy
Map<String, Object> properties = new HashMap<>();
[Link]("[Link]", "SCHEMA");
[Link]("hibernate.tenant_identifier_resolver",
tenantIdentifierResolver());
[Link]("hibernate.multi_tenant_connection_provider",
multiTenantConnectionProvider());

[Link](properties);
return em;
}

References
[1] InterviewBit. (2026). Top 40+ Spring Boot Interview Questions & Answers.
[Link]

[2] We Create Problems. (2026). 100+ Spring Boot Interview Questions and
Answers. [Link]
interview-questions

[11] GUVI. (2025). Spring Boot 3.x Interview Prep: Latest 50 Common
Questions. [Link]

[13] Hirist. (2026). Top 25+ Spring Security Interview Questions and Answers.
[Link]
answers/

[14] Java Bulletin. (2026). Spring Boot Interview Question – Improve API
Latency. [Link]
improve

You might also like