Spring Boot Interview Questions
Spring Boot Interview Questions
Table of Content
120 Most Important Spring Boot Interview Questions and Answers for Beginners............. 9
1. What is Spring Boot?........................................................................................................... 9
2. Why do we need Spring Boot?............................................................................................ 9
3. What are the main advantages of using Spring Boot?........................................................ 9
4. What are the key features of Spring Boot?........................................................................10
5. What is the difference between Spring and Spring Boot?................................................. 10
6. What are Spring Boot components?.................................................................................. 11
7. What is a Spring Boot Starter?.......................................................................................... 11
8. How does auto-configuration work in Spring Boot?........................................................... 11
9. What is Spring Boot Actuator?........................................................................................... 11
10. How can you create a Spring Boot application?.............................................................. 12
11. What is Spring Boot Initializer?........................................................................................ 12
12. What is the role of @SpringBootApplication annotation?................................................ 12
13. How does Spring Boot handle dependency management?.............................................12
14. What happens when a Spring Boot application is "Run as Java Application"?............... 13
15. What is an embedded server in Spring Boot?................................................................. 13
16. How can you change the default port of the embedded server?..................................... 13
17. What is Spring Boot CLI?................................................................................................ 14
18. What are the possible sources of external configuration in Spring Boot?....................... 14
19. What is the difference between an embedded container and a WAR file?......................15
20. How do you exclude auto-configuration classes in Spring Boot?.................................... 15
21. What is Spring Boot DevTools?....................................................................................... 15
22. How do you access command-line properties in Spring Boot?....................................... 16
23. What is the Spring Boot Actuator health endpoint?......................................................... 16
24. How do you secure Spring Boot Actuator endpoints?..................................................... 17
25. What is a multi-module project in Spring Boot?...............................................................17
26. What are profiles in Spring Boot?.................................................................................... 17
27. How do you configure logging in Spring Boot?................................................................18
28. What is a condition in Spring Boot auto-configuration?................................................... 18
29. What is Spring Boot's property binding?..........................................................................19
30. How can you implement custom health indicators in Spring Boot?................................. 19
31. What is Spring Boot's relaxed binding?........................................................................... 20
32. What is Spring Data JPA in Spring Boot?........................................................................ 20
33. How do you handle exceptions in Spring Boot REST applications?................................20
34. What is the @SpringBootTest annotation used for?........................................................ 21
35. How do you test a controller in Spring Boot?...................................................................21
36. What is the CommandLineRunner interface in Spring Boot?.......................................... 22
37. What is the difference between @Controller and @RestController?...............................23
● Auto-configuration
● Starter dependencies
● Externalized configuration
● Spring projects need separate server deployment, whereas Spring Boot includes
embedded servers
● Using an IDE with Spring Boot support like Spring Tool Suite or IntelliJ IDEA
● @ComponentScan: Tells Spring to look for components in the current package and its
sub-packages
This single annotation simplifies the setup process by replacing multiple annotations2.
16. How can you change the default port of the embedded
server?
You can change the default port (8080) of the embedded server by:
Example in application.properties:
text
server.port=9090
● Command-line arguments
● JNDI attributes
● OS environment variables
● @PropertySource annotations
● Default properties2
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
●
● Or using the excludeName attribute for string class names:
java
@EnableAutoConfiguration(excludeName={"org.springframework.boot.autoco
nfigure.jdbc.DataSourceAutoConfiguration"})
●
● Or in application.properties:
text
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jd
bc.DataSourceAutoConfiguration
●
Example:
java
@Component
public class MyBean {
@Value("${name}")
private String name;
}
management.endpoints.web.exposure.include=health,info
management.endpoints.web.exposure.exclude=env,beans
●
● Implementing custom security configurations for specific endpoints
● Environment variables
logging.level.root=WARN
logging.level.org.springframework.web=DEBUG
logging.file.name=myapp.log
●
● A logback-spring.xml file for more complex configurations
java
@Configuration
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
private String host;
private int port;
// getters and setters
}
This binds properties like mail.host and mail.port to the corresponding fields.
java
@Component
public class CustomHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws
Exception {
// Perform health check logic
if (/* everything is healthy */) {
builder.up().withDetail("details", "Everything is working
fine");
} else {
Example:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse>
handleResourceNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND",
ex.getMessage());
return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
}
}
java
@SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyIntegrationTest {
// test methods
}
@WebMvcTest(MyController.class)
public class MyControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testEndpoint() throws Exception {
mockMvc.perform(get("/api/items"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
●
● TestRestTemplate or WebTestClient for full integration tests with a running server
java
@Component
public class MyCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// Code to run on startup
System.out.println("Application started with arguments: " +
Arrays.toString(args));
}
}
@CrossOrigin(origins = "http://example.com")
@RestController
public class MyController { }
●
● Global configuration with WebMvcConfigurer:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://example.com")
.allowedMethods("GET", "POST");
}
}
●
spring.web.cors.allowed-origins=http://example.com
spring.web.cors.allowed-methods=GET,POST
●
● JCache (JSR-107)
● EhCache
● Hazelcast
● Infinispan
● Couchbase
● Redis
● Caffeine
You can enable caching with @EnableCaching and use annotations like @Cacheable,
@CachePut, and @CacheEvict on methods.
java
@GetMapping("/users")
public Page<User> getUsers(Pageable pageable) {
return userRepository.findAll(pageable);
}
text
/users?page=0&size=10&sort=lastName,desc
For REST APIs, you can also return a PagedResources/PagedModel object using Spring
HATEOAS to include navigation links.
● Using --debug when running the application from the command line
The report shows positive matches (configurations that were applied) and negative matches
(configurations that were not applied, with reasons).
● Creating a banner.txt file in the resources directory with your ASCII art
● Client applications: Register with the admin server using Spring Boot Admin Client
text
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.jpa.hibernate.ddl-auto=update
text
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
java
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile
file) {
try {
// Store or process the file
String originalFilename = file.getOriginalFilename();
byte[] bytes = file.getBytes();
// Save the file to disk, database, or cloud storage
return "File uploaded successfully: " + originalFilename;
} catch (Exception e) {
return "Failed to upload file: " + e.getMessage();
}
}
● Working with reactive streams using Project Reactor (Flux and Mono types)
java
@Configuration
public class I18nConfig {
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new
ResourceBundleMessageSource();
messageSource.setBasenames("messages");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
○ messages.properties (default)
○ messages_fr.properties (French)
java
@Autowired
private MessageSource messageSource;
java
@PostMapping("/users")
public ResponseEntity<User> createUser(@Valid @RequestBody User user)
{
// user is validated based on annotations like @NotNull, @Size, etc.
return ResponseEntity.ok(userService.save(user));
}
java
public class User {
@NotBlank(message = "Name is required")
private String name;
java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/public/**").permitAll()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.httpBasic();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws
Exception {
auth.inMemoryAuthentication()
.withUser("user").password("{noop}password").roles("USER")
3. Use @PreAuthorize and other security annotations for method-level security
text
app.name=My Application
app.description=A Spring Boot application
app.max-connections=100
java
@Service
public class MyService {
private final AppProperties appProperties;
● /static
● /public
● /resources
text
spring.web.resources.static-locations=classpath:/custom/,classpath:/st
atic/
spring.mvc.static-path-pattern=/resources/**
java
@Component
public class ScheduledTasks {
You can schedule tasks with fixed rates, fixed delays, or using cron expressions.
java
@Service
public class AsyncService {
@Async
public CompletableFuture<String> asyncMethod() {
// Simulating long processing
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Async result");
}
}
java
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new
ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
●
● As a traditional WAR file in an external application server:
java
●
● Using Docker containerization
● Using cloud platforms like AWS, Azure, Google Cloud Platform, or Heroku
● Spring Test & Spring Boot Test (utilities and integration support)
You can use annotations like @SpringBootTest for full integration tests and @WebMvcTest,
@DataJpaTest for more focused tests.
info.app.name=My App
info.app.description=My Spring Boot Application
info.app.version=1.0.0
●
● InfoContributor beans:
java
@Component
public class CustomInfoContributor implements InfoContributor {
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("database", Map.of(
"type", "mysql",
"status", "connected"
));
}
}
●
○ application-dev.properties
○ application-prod.properties
○ application-test.properties
○ In application.properties: spring.profiles.active=dev
@Configuration
@Profile("dev")
public class DevConfig {
// dev-specific beans
}
@Configuration
@Profile("prod")
public class ProdConfig {
// prod-specific beans
}
4.
● JPA (Java Persistence API) is a specification that defines a set of interfaces for ORM in
Java applications.
Spring Boot uses Hibernate as the default JPA implementation when you include
spring-boot-starter-data-jpa. You work with JPA interfaces like EntityManager and Repository,
but behind the scenes, Hibernate handles the actual database operations2.
1. Flyway:
2. Liquibase:
○ Configure in application.properties:
text
spring.liquibase.change-log=classpath:db/changelog/db.changelog-master
.xml
○
These tools help version control database changes and ensure consistent schema across
environments.
3.
4. Spring Data REST automatically creates endpoints for CRUD operations:
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // Often disabled for REST APIs
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/**").authenticated()
.and()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS); // RESTful
APIs are typically stateless
}
}
3.
4. For token-based authentication, integrate JWT or OAuth2:
Example:
java
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleGenericException(Exception ex) {
return new ErrorResponse("SERVER_ERROR", "An unexpected error
occurred");
}
}
xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/>
</parent>
@Service
public class UserService {
private final UserRepository userRepository;
@Transactional
public User createUser(User user) {
// Perform operations within a transaction
return userRepository.save(user);
}
}
3.
4. Configuring transaction attributes:
java
@Transactional(readOnly = true)
public List<User> getAllUsers() {
return userRepository.findAll();
}
5.
● MongoDB: spring-boot-starter-data-mongodb
● Redis: spring-boot-starter-data-redis
● Cassandra: spring-boot-starter-data-cassandra
● Elasticsearch: spring-boot-starter-data-elasticsearch
● Neo4j: spring-boot-starter-data-neo4j
For each, Spring Boot auto-configures the necessary components and provides repository
support through Spring Data. Example for MongoDB:
java
@Document
public class Product {
@Id
private String id;
private String name;
private double price;
// getters and setters
}
@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.
3. Using the excludeName attribute for string class names:
java
@SpringBootApplication(excludeName={"org.springframework.boot.autoconf
igure.jdbc.DataSourceAutoConfiguration"})
public class Application {
// ...
}
4.
5. Using configuration properties:
text
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jd
bc.DataSourceAutoConfiguration
6.
2. java
@Service
public class MessageService {
private final JmsTemplate jmsTemplate;
3.
4. AMQP (Advanced Message Queuing Protocol):
5. Kafka:
○ Dependency: spring-boot-starter-kafka
Each integrates with Spring's messaging abstractions for sending and receiving messages.
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnMissingBean(MyService.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnProperty(prefix = "my.service", name = "enabled",
havingValue = "true", matchIfMissing = true)
public MyService myService() {
return new MyServiceImpl();
}
}
2.
3. Create a file named META-INF/spring.factories in your resources:
text
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
4.
5. Apply conditions to control when your configuration is applied
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private boolean enabled = true;
// getters and setters
7.
@Service
public class ApiService {
private final WebClient webClient;
3.
4. Customize WebClient with a bean:
java
@Bean
5.
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
public void shouldReturnUserWhenFound() {
// Arrange
User expectedUser = new User("1", "John");
when(userRepository.findById("1")).thenReturn(Optional.of(expectedUser
));
// Act
User actualUser = userService.getUserById("1");
// Assert
assertEquals(expectedUser, actualUser);
verify(userRepository).findById("1");
3.
4. For controller unit tests, use MockMvc with @WebMvcTest:
java
@WebMvcTest(UserController.class)
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Test
public void shouldReturnUser() throws Exception {
// Arrange
User user = new User("1", "John");
when(userService.getUserById("1")).thenReturn(user);
5.
@SpringBootApplication
@EnableCaching
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3.
4. Use caching annotations on methods:
java
@Service
public class UserService {
@Cacheable("users")
public User getUserById(String id) {
// Method will only be called if result not in cache
return fetchUserFromDatabase(id);
}
@CacheEvict("users")
public void updateUser(User user) {
// Update user in database and evict from cache
saveUserToDatabase(user);
}
5.
// URL: /users?name=John&age=25
@GetMapping("/users")
public List<User> getUsers(@RequestParam String name, @RequestParam
int age) {
// Access name and age values
}
●
● @PathVariable extracts values from the path segments in the URL:
java
// URL: /users/123/orders/456
@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(@PathVariable Long userId, @PathVariable Long
orderId) {
// Access userId and orderId values
}
●
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBeforeMethodExecution(JoinPoint joinPoint) {
logger.info("Executing: " +
joinPoint.getSignature().getName());
}
@AfterReturning(pointcut = "execution(*
com.example.service.*.*(..))", returning = "result")
public void logAfterMethodExecution(JoinPoint joinPoint, Object
result) {
logger.info("Completed: " + joinPoint.getSignature().getName()
+ " with result: " + result);
}
@Around("@annotation(com.example.annotation.Timed)")
public Object timeMethod(ProceedingJoinPoint joinPoint) throws
Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
long elapsedTime = System.currentTimeMillis() - start;
logger.info(joinPoint.getSignature().getName() + " executed in
" + elapsedTime + "ms");
return result;
}
}
3.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed {
}
5.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneNumberValidator.class)
public @interface ValidPhoneNumber {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
2.
3. Implement the validator:
java
@Override
public void initialize(ValidPhoneNumber constraintAnnotation) {
}
4.
5. Use the custom validator in your model:
java
@ValidPhoneNumber
private String phoneNumber;
6.
java
@Configuration
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
private String host;
private int port = 25; // default value
text
mail.host=smtp.example.com
mail.port=587
mail.username=user
mail.password=secret
[email protected]
Will be automatically bound to the fields in the MailProperties class, with type conversion and
validation.
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user_service", r -> r
.path("/users/**")
.filters(f -> f
.requestRateLimiter(c -> c
.setRateLimiter(redisRateLimiter())
.setKeyResolver(userKeyResolver()))
)
2.
3. Using Bucket4j with Spring Boot:
java
@Component
public class RateLimitingInterceptor implements HandlerInterceptor {
private final Map<String, Bucket> buckets = new
ConcurrentHashMap<>();
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) {
String ipAddress = request.getRemoteAddr();
Bucket bucket = buckets.computeIfAbsent(ipAddress,
k -> Bucket4j.builder()
.addLimit(Bandwidth.simple(10, Duration.ofMinutes(1)))
.build());
4.
5. Using custom annotations and AOP:
java
@Target(ElementType.METHOD)
@Aspect
@Component
public class RateLimitingAspect {
private final Map<String, RateLimiter> rateLimiters = new
ConcurrentHashMap<>();
@Around("@annotation(rateLimit)")
public Object enforceRateLimit(ProceedingJoinPoint joinPoint,
RateLimit rateLimit) throws Throwable {
String methodKey = joinPoint.getSignature().toLongString();
RateLimiter rateLimiter =
rateLimiters.computeIfAbsent(methodKey,
k -> RateLimiter.create(rateLimit.limit() / (double)
rateLimit.duration()));
if (!rateLimiter.tryAcquire()) {
throw new TooManyRequestsException("Rate limit exceeded");
}
return joinPoint.proceed();
}
}
6.
@GetMapping("/not-found")
@ResponseStatus(HttpStatus.NOT_FOUND)
public void notFound() {
// Method implementation
}
2.
3. On exception classes for exception handling:
java
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundException(String message) {
super(message);
}
}
4.
5. When this exception is thrown from a controller method, the response status will
automatically be set to 404 Not Found.
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements
WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry)
{
registry.addEndpoint("/websocket")
.setAllowedOrigins("*")
.withSockJS(); // Fallback options if WebSocket isn't
available
}
}
3.
4. Create a controller for handling WebSocket messages:
java
@Controller
public class ChatController {
@MessageMapping("/chat.send")
@SendTo("/topic/messages")
public ChatMessage send(ChatMessage message) {
return message;
}
@MessageMapping("/chat.join")
@SendTo("/topic/users")
public UserEvent join(UserEvent event) {
return new UserEvent(event.getUsername(), "joined");
}
}
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.17.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.17.2</version>
<scope>test</scope>
</dependency>
2.
3. Create integration tests:
java
@SpringBootTest
@Testcontainers
public class UserServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new
PostgreSQLContainer<>("postgres:14")
.withDatabaseName("testdb")
.withUsername("test")
@DynamicPropertySource
static void postgresProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username",
postgres::getUsername);
registry.add("spring.datasource.password",
postgres::getPassword);
}
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@Test
void shouldSaveUser() {
// Arrange
User user = new User(null, "John");
// Act
User savedUser = userService.createUser(user);
// Assert
assertNotNull(savedUser.getId());
assertEquals("John", savedUser.getName());
// Verify in database
Optional<User> foundUser =
userRepository.findById(savedUser.getId());
assertTrue(foundUser.isPresent());
assertEquals("John", foundUser.get().getName());
}
}
4.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.
3. Define a GraphQL schema in src/main/resources/graphql/schema.graphqls:
graphql
type Query {
bookById(id: ID!): Book
allBooks: [Book]
}
type Book {
id: ID!
title: String!
author: Author!
pages: Int
}
type Author {
id: ID!
name: String!
books: [Book]
}
4.
Copyright © 2025 by SkillForgePrep
5. Create a controller:
java
@Controller
public class BookController {
@QueryMapping
public Book bookById(@Argument ID id) {
return bookRepository.findById(id).orElse(null);
}
@QueryMapping
public List<Book> allBooks() {
return bookRepository.findAll();
}
@SchemaMapping
public Author author(Book book) {
return book.getAuthor();
}
}
6.
7. Access the GraphQL endpoint at /graphql
@Component
public class RequestLoggingFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse)
response;
chain.doFilter(request, response);
2.
3. Using Spring's built-in request logger:
text
# application.properties
4.
5. java
@Bean
public CommonsRequestLoggingFilter requestLoggingFilter() {
CommonsRequestLoggingFilter loggingFilter = new
CommonsRequestLoggingFilter();
loggingFilter.setIncludeClientInfo(true);
loggingFilter.setIncludeQueryString(true);
loggingFilter.setIncludePayload(true);
loggingFilter.setMaxPayloadLength(10000);
loggingFilter.setIncludeHeaders(true);
return loggingFilter;
}
6.
7. Using Spring AOP:
java
@Aspect
@Component
public class RequestLoggingAspect {
@Around("@annotation(org.springframework.web.bind.annotation.RequestMa
pping) || " +
"@annotation(org.springframework.web.bind.annotation.GetMapping) || "
+
"@annotation(org.springframework.web.bind.annotation.PostMapping)")
public Object logRequest(ProceedingJoinPoint joinPoint) throws
Throwable {
return result;
}
}
8.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ModelAndView
handleResourceNotFound(ResourceNotFoundException ex) {
ModelAndView modelAndView = new
ModelAndView("error/not-found");
modelAndView.addObject("message", ex.getMessage());
return modelAndView;
@ExceptionHandler(Exception.class)
public ModelAndView handleGenericException(Exception ex) {
ModelAndView modelAndView = new ModelAndView("error/generic");
modelAndView.addObject("message", "An unexpected error
occurred");
return modelAndView;
}
}
2.
3. Global model attributes:
java
@ControllerAdvice
public class GlobalModelAttributes {
@ModelAttribute("categories")
public List<String> getCategories() {
return Arrays.asList("Electronics", "Books", "Clothing");
}
}
4.
5. Global data binding:
java
@ControllerAdvice
public class GlobalDataBinder {
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Date.class, new
CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
}
}
6.
Copyright © 2025 by SkillForgePrep
86. How do you implement HATEOAS in Spring Boot?
HATEOAS (Hypermedia as the Engine of Application State) can be implemented in Spring Boot
using Spring HATEOAS:
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public EntityModel<User> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
return EntityModel.of(user,
linkTo(methodOn(UserController.class).getUser(id)).withSelfRel(),
linkTo(methodOn(UserController.class).getAllUsers()).withRel("all-user
s"),
linkTo(methodOn(OrderController.class).getOrdersByUserId(id)).withRel(
"orders")
);
}
@GetMapping
public CollectionModel<EntityModel<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
linkTo(methodOn(UserController.class).getUser(user.getId())).withSelfR
el(),
linkTo(methodOn(OrderController.class).getOrdersByUserId(user.getId())
).withRel("orders")
))
.collect(Collectors.toList());
return CollectionModel.of(userModels,
linkTo(methodOn(UserController.class).getAllUsers()).withSelfRel());
}
}
3.
4. The JSON response will include links to related resources
● @RequestBody: Indicates that a method parameter should be bound to the body of the
HTTP request. Spring automatically deserializes the HTTP request body into the Java
object.
java
@PostMapping("/users")
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
●
@GetMapping("/users/{id}")
@ResponseBody
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
●
Note: When using @RestController, @ResponseBody is implied for all methods and doesn't
need to be explicitly added.
java
@Configuration
public class AppConfig {
@Bean
@ConditionalOnProperty(name = "feature.legacy", havingValue =
"true")
public LegacyService legacyService() {
return new LegacyServiceImpl();
}
@Bean
@ConditionalOnClass(name = "com.example.ExternalClass")
public ExternalService externalService() {
return new ExternalServiceImpl();
}
@Bean
@ConditionalOnMissingBean
@Bean
@ConditionalOnExpression("${app.enable-feature} and
'${app.feature-mode}'=='advanced'")
public AdvancedFeatureService advancedFeatureService() {
return new AdvancedFeatureServiceImpl();
}
@Bean
@ConditionalOnWebApplication
public WebHelperService webHelperService() {
return new WebHelperServiceImpl();
}
@Bean
@Profile("dev")
public DevToolsService devToolsService() {
return new DevToolsServiceImpl();
}
}
You can also create custom conditions by implementing the Condition interface:
java
public class OnCustomCondition implements Condition {
@Override
public boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("custom.property") &&
"enabled".equals(env.getProperty("custom.property"));
}
}
@Conditional(OnCustomCondition.class)
2.
3. Publish the event:
java
@Service
public class UserService {
private final UserRepository userRepository;
private final ApplicationEventPublisher eventPublisher;
4.
5. Listen for events:
java
@Component
public class UserEventListener {
@EventListener
public void handleUserCreatedEvent(UserCreatedEvent event) {
logger.info("User created: {}", event.getUser().getId());
// Send welcome email, etc.
}
@EventListener
@Async
public void handleUserCreatedEventAsync(UserCreatedEvent event) {
// Async processing
}
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleUserCreatedEventTransactional(UserCreatedEvent
event) {
// Only process after transaction commits
6.
○ WebFlux DSL
○ Coroutines support
kotlin
@SpringBootApplication
class MyApplication
@RestController
class UserController(private val userService: UserService) {
@GetMapping("/users/{id}")
fun getUser(@PathVariable id: Long): User {
return userService.getUserById(id)
@GetMapping("/users")
fun getUsers(): List<User> {
return userService.getAllUsers()
}
}
@Service
class UserService(private val userRepository: UserRepository) {
app.service.enabled=true
app.service.timeout=30
4.
app.my-list[0]=value1
app.my-list[1]=value2
6.
7. or using comma separation:
text
app.my-list=value1,value2
8.
9. Map properties:
text
app.my-map.key1=value1
app.my-map.key2=value2
10.
11.Duration properties (converted automatically):
text
app.timeout=30s
app.connection-timeout=PT10S
12.
13.Size properties:
text
app.max-file-size=10MB
14.
java
@ImportAutoConfiguration(DataSourceAutoConfiguration.class)
@SpringBootTest
public class DatabaseTest {
@Autowired
private DataSource dataSource;
@Test
public void testDataSource() {
assertNotNull(dataSource);
// More tests
}
}
This allows you to precisely control which auto-configurations are applied without relying on the
full auto-configuration mechanism.
java
@Component
public class MyApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("Application started with the following
options:");
ApplicationRunner beans are automatically called by Spring Boot after the application is fully
started.
my-spring-boot-starter/
├── pom.xml
└── src/
└── main/
├── java/
│ └── com/example/starter/
│ ├── MyAutoConfiguration.java
│ ├── MyService.java
│ └── MyServiceProperties.java
└── resources/
└── META-INF/
├── spring.factories
└── additional-spring-configuration-metadata.json
<groupId>com.example</groupId>
<artifactId>my-spring-boot-starter</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
4.
5. Create configuration properties:
java
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private boolean enabled = true;
private String prefix = "Default";
6.
8.
9. Create auto-configuration:
java
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "my.service", name = "enabled",
havingValue = "true", matchIfMissing = true)
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getPrefix());
}
}
10.
11.Create spring.factories file:
text
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
12.
13.Create additional-spring-configuration-metadata.json:
json
{
"properties": [
{
"name": "my.service.enabled",
"type": "java.lang.Boolean",
"description": "Enable my service.",
"defaultValue": true
},
{
"name": "my.service.prefix",
"type": "java.lang.String",
"description": "Message prefix for my service.",
"defaultValue": "Default"
}
]
}
14.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2.
3. Configure tracing in application.properties:
text
spring.application.name=my-service
spring.sleuth.sampler.probability=1.0
spring.zipkin.base-url=http://localhost:9411
4.
5. Tracing is automatically added to:
○ HTTP requests
○ RestTemplate calls
○ Async operations
@Service
public class OrderService {
7.
<dependency>
<groupId>org.springframework.cloud</groupId>
2.
3. Configure circuit breakers:
java
@Configuration
public class CircuitBreakerConfig {
@Bean
public Customizer<Resilience4JCircuitBreakerFactory>
defaultCustomizer() {
return factory -> factory.configureDefault(id -> new
Resilience4JConfigBuilder(id)
.timeLimiterConfig(TimeLimiterConfig.custom().timeoutDuration(Duration
.ofSeconds(4)).build())
.circuitBreakerConfig(CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.slidingWindowSize(10)
.permittedNumberOfCallsInHalfOpenState(3)
.build())
.build());
}
}
4.
5. Use circuit breaker in service:
java
@Service
public class UserService {
6.
7. Alternatively, use the annotation-based approach:
java
@Service
public class UserService {
8.
@ConfigurationProperties(prefix = "app")
public class AppProperties {
2.
3. Configure in application.properties:
text
app.server.host=example.com
app.server.port=9090
app.server.secure=true
app.client.timeout=60
app.client.retry-count=5
4.
5. Use the configuration:
java
@Service
public class AppService {
6.
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
false);
mapper.registerModule(new JavaTimeModule());
return mapper;
}
}
3.
spring.jackson.serialization.indent_output=true
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.default-property-inclusion=non_null
spring.jackson.deserialization.fail-on-unknown-properties=false
5.
6. Customizing serialization for specific classes:
java
@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate birthDate;
7.
8. Creating custom serializers and deserializers:
java
gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
// Register with:
SimpleModule module = new SimpleModule();
module.addSerializer(LocalDate.class, new CustomDateSerializer());
objectMapper.registerModule(module);
9.
2.
3. Implement the controller:
java
@RestController
@RequestMapping("/products")
public class ProductController {
@GetMapping
public Page<Product> getProducts(
return productRepository.findAll(pageable);
}
@GetMapping("/by-category")
public Page<Product> getProductsByCategory(
@RequestParam String category,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
4.
5. Clients can request pages with:
text
/products?page=0&size=10&sort=price,desc
6.
7. Add HATEOAS support for pagination:
java
@RestController
@RequestMapping("/products")
public class ProductController {
@GetMapping
public ResponseEntity<PagedModel<EntityModel<Product>>>
getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id,asc") String[] sort,
PagedResourcesAssembler<Product> assembler) {
Page<Product> productPage =
productRepository.findAll(pageable);
return ResponseEntity.ok(
assembler.toModel(productPage,
product -> EntityModel.of(product,
linkTo(methodOn(ProductController.class).getProduct(product.getId())).
withSelfRel())
)
);
}
@GetMapping("/{id}")
public EntityModel<Product> getProduct(@PathVariable Long id) {
Product product = productRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Product
not found"));
return EntityModel.of(product,
8.
1. Default behavior: Spring Boot uses HikariCP as the default connection pool when
available on the classpath.
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
3.
4. Using a different connection pool:
○ Add the dependency for the desired pool (e.g., Tomcat JDBC or Apache DBCP2)
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.hikari")
public HikariConfig hikariConfig() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setMaximumPoolSize(10);
return config;
}
@Bean
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
}
6.
7. Monitoring connection pool:
○ Enable with:
text
management.endpoints.web.exposure.include=health,info,metrics
management.metrics.enable.jdbc=true
○
○ Access at: /actuator/metrics/hikaricp.connections.active
spring.mvc.contentnegotiation.favor-parameter=true
spring.mvc.contentnegotiation.parameter-name=format
spring.mvc.contentnegotiation.media-types.json=application/json
spring.mvc.contentnegotiation.media-types.xml=application/xml
2.
3. Implementation in controllers:
java
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id);
}
}
4.
5. With this setup, clients can request different formats:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void
configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer
.favorParameter(true)
.parameterName("format")
.ignoreAcceptHeader(false)
.useRegisteredExtensionsOnly(false)
.defaultContentType(MediaType.APPLICATION_JSON)
.mediaType("json", MediaType.APPLICATION_JSON)
.mediaType("xml", MediaType.APPLICATION_XML)
.mediaType("pdf", MediaType.APPLICATION_PDF);
}
}
7.
@Configuration
@Bean
@Conditional(MyCondition.class)
public MyService myService() {
return new MyServiceImpl();
}
}
2.
3. Spring Boot provides specialized conditional annotations:
4. These are crucial for Spring Boot's auto-configuration mechanism, which applies
configuration only when needed.
@Service
public class FileStorageService {
public FileStorageService(@Value("${file.storage.location}")
String storageLocation) {
this.storageLocation =
Paths.get(storageLocation).toAbsolutePath().normalize();
try {
Files.createDirectories(this.storageLocation);
} catch (IOException e) {
throw new RuntimeException("Could not create storage
directory", e);
}
}
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("Could not read file: " +
filename);
}
} catch (Exception e) {
throw new RuntimeException("Could not read file: " +
filename, e);
}
}
}
2.
3. Cloud storage (AWS S3 example):
java
public S3FileStorageService(
AmazonS3 s3Client,
@Value("${aws.s3.bucket}") String bucketName) {
this.s3Client = s3Client;
this.bucketName = bucketName;
}
s3Client.putObject(new PutObjectRequest(
bucketName, filename, file.getInputStream(), metadata
).withCannedAcl(CannedAccessControlList.PublicRead));
4.
5. Database storage (for small files):
java
@Entity
public class FileEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Lob
private byte[] data;
@Service
public class DatabaseFileStorageService {
6.
@Configuration
@EnableBatchProcessing
public class BatchConfig {
@Bean
public ItemReader<Transaction> reader() {
@Bean
public LineMapper<Transaction> lineMapper() {
DefaultLineMapper<Transaction> lineMapper = new
DefaultLineMapper<>();
lineMapper.setLineTokenizer(tokenizer);
lineMapper.setFieldSetMapper(fieldSetMapper);
return lineMapper;
}
@Bean
public ItemProcessor<Transaction, Transaction> processor() {
return transaction -> {
// Process transaction (e.g., convert currency, validate)
transaction.setDescription(transaction.getDescription().toUpperCase())
;
return transaction;
};
}
@Bean
public Step step1() {
return stepBuilderFactory.get("step1")
.<Transaction, Transaction>chunk(10)
.reader(reader())
.processor(processor())
.writer(writer())
.build();
}
@Bean
public Job importTransactionsJob(JobCompletionNotificationListener
listener) {
return jobBuilderFactory.get("importTransactionsJob")
.incrementer(new RunIdIncrementer())
.listener(listener)
.flow(step1())
.end()
.build();
}
}
3.
@Component
public class JobCompletionNotificationListener extends
JobExecutionListenerSupport {
@Override
public void afterJob(JobExecution jobExecution) {
if(jobExecution.getStatus() == BatchStatus.COMPLETED) {
logger.info("Job finished!
Rate limiting controls the number of requests a client can make to an API within a specific
timeframe. Use Spring Cloud Gateway or Bucket4j with Spring Boot:
java
@Bean
text
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=10, 1,
SECONDS
Spring Boot integrates with Flyway or Liquibase for database version control. Add
spring-boot-starter-flyway and place SQL scripts in
src/main/resources/db/migration. On startup, Flyway applies pending migrations6.
java
Use Spring Cloud Config Server with encryption or Jasypt for property encryption:
text
spring.datasource.password=ENC(encryptedPassword)
@Override
registry.addMapping("/api/**")
.allowedOrigins("https://example.com")
.allowedMethods("GET", "POST");
It provides default Maven configurations, dependency management, and plugin settings. Child
POMs inherit versions for Spring Boot dependencies, reducing conflicts6.
java
@Bean
This annotation loads the full application context for integration testing:
java
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@Autowired
text
spring:
security:
oauth2:
client:
registration:
client-id: your-client-id
client-secret: your-secret
java
@Component
@Override
// Initialization logic
java
@Bean
@ConfigurationProperties("app.datasource.primary")
return DataSourceBuilder.create().build();
@Bean
@ConfigurationProperties("app.datasource.secondary")
return DataSourceBuilder.create().build();
java
@ConfigurationProperties(prefix = "mail")
Use MultipartFile:
java
@PostMapping("/upload")
text
spring.servlet.multipart.max-file-size=10MB
java
@Component
@Override
java
@Cacheable("products")
return productRepository.findById(id);
Create a Dockerfile:
text
FROM openjdk:17-jdk-slim
Copyright © 2025 by SkillForgePrep
COPY target/app.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Build with docker build -t spring-app . and run with docker run -p 8080:8080
spring-app8.
java
@Async
// Async logic
java
java
@RestControllerAdvice
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<Error>
handleNotFound(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new
Error(ex.getMessage()));
29. What are slice tests in Spring Boot and how do they
work?
Slice tests in Spring Boot load only a specific "slice" of the application context to test a particular
layer in isolation. Examples include @WebMvcTest (web layer), @DataJpaTest (persistence
layer), @JsonTest (JSON serialization), and @RestClientTest (REST clients). Each slice
auto-configures the minimum required components for testing that slice while mocking or
disabling other components. This approach speeds up tests by loading only what's necessary
and allows focused testing of individual architectural layers.
57. What is the API Gateway pattern and how does Spring
Cloud Gateway implement it?
The API Gateway pattern provides a single entry point for all client requests to a microservices
system, handling cross-cutting concerns like routing, filtering, security, and monitoring. Spring
Cloud Gateway implements this using a reactive, non-blocking architecture built on Spring
WebFlux. It routes requests based on predicates (conditions about the request) and applies
filters (to modify requests/responses). Gateway features include path-based routing, load
balancing, circuit breaking via Resilience4j, rate limiting, path rewriting, and request/response
modification. This centralizes common functionality and shields clients from internal service
composition.
java
@Component
public class ExternalServiceHealthIndicator extends
AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws
Exception {
try {
// Check external service health
if (isExternalServiceUp()) {
builder.up().withDetail("service", "External Service
is responding normally");
} else {
builder.down().withDetail("service", "External Service
is not responding");
}
} catch (Exception e) {
builder.down().withDetail("error", e.getMessage());
}
}
}
66. How can you collect and analyze metrics from Spring
Boot applications?
java
@SpringBootTest
@Testcontainers
class OrderServiceIntegrationTest {
@Container
static PostgreSQLContainer<?> postgres = new
PostgreSQLContainer<>("postgres:14")
.withDatabaseName("test")
@DynamicPropertySource
static void registerPgProperties(DynamicPropertyRegistry registry)
{
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username",
postgres::getUsername);
registry.add("spring.datasource.password",
postgres::getPassword);
}
@Test
void testOrderCreation() {
// Test using real PostgreSQL database
}
}
94. What is the role of test slices in Spring Boot and when
would you use them?
Test slices in Spring Boot load only specific parts of the application context to test particular
layers in isolation, improving test performance. Examples include @WebMvcTest (web layer),
@DataJpaTest (repository layer), @JsonTest (JSON serialization), and @RestClientTest (REST
clients). Use test slices when you need to test a specific layer without the overhead of loading
the entire application. For instance, use @WebMvcTest to test controllers with mocked services,
@DataJpaTest to test repositories with an actual database, or @JsonTest to verify object
serialization. They provide focused environments with appropriate auto-configurations while
excluding unrelated components.
java
@Component
public class RequestResponseLoggingFilter extends OncePerRequestFilter
{
private static final Logger log =
LoggerFactory.getLogger(RequestResponseLoggingFilter.class);
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws
ServletException, IOException {
ContentCachingRequestWrapper requestWrapper = new
ContentCachingRequestWrapper(request);
ContentCachingResponseWrapper responseWrapper = new
ContentCachingResponseWrapper(response);
java
@Component
public class ApiKeyAuthFilter extends
AbstractAuthenticationProcessingFilter {
public ApiKeyAuthFilter() {
super(new AntPathRequestMatcher("/**"));
setAuthenticationSuccessHandler((request, response,
authentication) -> {
SecurityContextHolder.getContext().setAuthentication(authentication);
chain.doFilter(request, response);
});
setAuthenticationFailureHandler((request, response, exception)
-> {
response.setStatus(HttpStatus.UNAUTHORIZED.value());
@Override
public Authentication attemptAuthentication(HttpServletRequest
request,
HttpServletResponse
response) {
String apiKey = request.getHeader("X-API-Key");
if (apiKey == null || apiKey.isEmpty()) {
throw new AuthenticationCredentialsNotFoundException("No
API key found");
}