0% found this document useful (0 votes)
31 views63 pages

Spring Boot & Design Patterns Interview Q&A Guide

The document is a comprehensive interview guide covering key concepts, annotations, and differences between Spring and Spring Boot, including auto-configuration, RESTful API development, exception handling, and various HTTP methods. It also discusses Spring Boot advantages, project setup, and dependency injection methods. Additionally, it highlights the differences between various components like Repository vs DAO, and Controller vs Repository.

Uploaded by

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

Spring Boot & Design Patterns Interview Q&A Guide

The document is a comprehensive interview guide covering key concepts, annotations, and differences between Spring and Spring Boot, including auto-configuration, RESTful API development, exception handling, and various HTTP methods. It also discusses Spring Boot advantages, project setup, and dependency injection methods. Additionally, it highlights the differences between various components like Repository vs DAO, and Controller vs Repository.

Uploaded by

divyavyas003
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 63

Spring Boot & Design Patterns Interview Q&A Guide

Spring Boot Framework Questions

1. What is Spring Boot?


Spring Boot is an open-source Java framework that simplifies the development of stand-alone,
production-grade Spring-based applications. It provides auto-configuration, embedded servers, and
starter dependencies to reduce boilerplate code and configuration.

2. What is @SpringBootApplication annotation?


@SpringBootApplication is a composite annotation that combines three annotations:

@Configuration - Marks the class as a configuration class


@EnableAutoConfiguration - Enables Spring Boot's auto-configuration mechanism
@ComponentScan - Enables component scanning in the current package and sub-packages

java

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

3. Difference between @RestController & @Controller


@Controller: Used for traditional MVC controllers that return view names. Requires @ResponseBody
for JSON responses.

@RestController: Combination of @Controller + @ResponseBody. Automatically serializes return


objects to JSON/XML.
java

@Controller
public class WebController {
@RequestMapping("/home")
@ResponseBody
public String home() {
return "Hello World";
}
}

@RestController
public class ApiController {
@RequestMapping("/api/home")
public String home() {
return "Hello World"; // Automatically converted to JSON
}
}

4. Difference between @RequestMapping and @GetMapping


@RequestMapping: Generic mapping annotation that can handle all HTTP methods

@GetMapping: Specialized annotation specifically for GET requests (shortcut for


@RequestMapping(method = RequestMethod.GET))

java

@RequestMapping(value = "/users", method = RequestMethod.GET)


public List<User> getUsers() { }

@GetMapping("/users") // Equivalent to above


public List<User> getUsers() { }

5. What is difference between Spring and Spring Boot?


Spring Spring Boot

Requires manual configuration Auto-configuration

XML/Java configuration needed Convention over configuration

No embedded server Embedded server (Tomcat, Jetty)

Manual dependency management Starter dependencies

More boilerplate code Minimal boilerplate


 

6. What is @PathVariable?
@PathVariable is used to extract values from the URI path and bind them to method parameters.
java

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}

7. What is @PostMapping and @GetMapping?


@GetMapping: Used for retrieving data (HTTP GET requests)
@PostMapping: Used for creating new resources (HTTP POST requests)

java

@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }

@PostMapping("/users")
public User createUser(@RequestBody User user) { }

8. Difference between REST and SOAP


REST SOAP

Architectural style Protocol

Uses HTTP methods Uses XML messaging

Lightweight Heavy

JSON/XML support Only XML

Stateless Can be stateful

Better performance More overhead


 

9. Why use Spring Boot when we have Spring?


Rapid Development: Auto-configuration reduces setup time

Embedded Servers: No need for external server deployment


Starter Dependencies: Simplified dependency management

Production Ready: Built-in monitoring and health checks

Minimal Configuration: Convention over configuration approach

10. How does RestController know which class to start from?


Spring Boot uses the @SpringBootApplication annotation to scan for components. The main class with
@SpringBootApplication serves as the entry point, and Spring Boot automatically discovers and registers
all @RestController classes within the package and its sub-packages.

11. How to handle exceptions in Spring Boot?

java

@ControllerAdvice
public class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
ErrorResponse error = new ErrorResponse("NOT_FOUND", ex.getMessage());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}

@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
ErrorResponse error = new ErrorResponse("INTERNAL_ERROR", "Something went wrong");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}

12. How to write RESTful API?


java

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

@Autowired
private UserService userService;

@GetMapping
public List<User> getAllUsers() {
return userService.findAll();
}

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id);
}

@PostMapping
public User createUser(@RequestBody User user) {
return userService.save(user);
}

@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}

@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.delete(id);
}
}

13. How to create custom query in Spring Boot repository?


java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

@Query("SELECT u FROM User u WHERE u.email = ?1")


User findByEmail(String email);

@Query(value = "SELECT * FROM users WHERE age > ?1", nativeQuery = true)
List<User> findUsersOlderThan(int age);

@Modifying
@Query("UPDATE User u SET u.active = false WHERE u.id = ?1")
void deactivateUser(Long id);
}

14. How to update information in Spring Boot?

java

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

public User updateUser(Long id, User updatedUser) {


User existingUser = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));

existingUser.setName(updatedUser.getName());
existingUser.setEmail(updatedUser.getEmail());

return userRepository.save(existingUser);
}
}

15. POST and PUT difference


POST PUT

Creates new resource Updates existing resource

Not idempotent Idempotent

Server decides resource ID Client specifies resource ID

201 Created status 200 OK or 204 No Content


 
16. @Qualifier annotation

Used to resolve ambiguity when multiple beans of the same type exist.

java

@Service
@Qualifier("emailService")
public class EmailNotificationService implements NotificationService { }

@Service
@Qualifier("smsService")
public class SMSNotificationService implements NotificationService { }

@RestController
public class NotificationController {

@Autowired
@Qualifier("emailService")
private NotificationService notificationService;
}

17. How to disable auto-configuration in Spring Boot?

java

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

// Or using properties
# application.properties
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigur

 

18. Spring Boot advantages


Auto-configuration: Automatically configures beans based on classpath

Embedded servers: No need for external server setup


Starter dependencies: Simplified dependency management

Production ready: Built-in monitoring and health checks

Microservices ready: Easy to create standalone applications

Testing support: Comprehensive testing framework

19. Key Spring Boot annotations


@SpringBootApplication: Main application class
@RestController: REST API controller
@Service: Service layer component

@Repository: Data access layer component


@Configuration: Configuration class

@Autowired: Dependency injection


@Value: Inject property values
@Profile: Profile-specific components

20. Can we change the port number in Spring Boot?


Yes, using application.properties:

properties

server.port=8081

Or programmatically:

java

@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
System.setProperty("server.port", "8081");
SpringApplication.run(MyApplication.class, args);
}
}

21. XML output in Spring Boot controller

java

@RestController
public class UserController {

@GetMapping(value = "/users/{id}", produces = MediaType.APPLICATION_XML_VALUE)


public User getUserAsXml(@PathVariable Long id) {
return userService.findById(id);
}
}

Add Jackson XML dependency:


xml

<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>

22. Key components of Spring Boot


Auto-configuration: Automatic bean configuration
Starter Dependencies: Pre-configured dependency sets

Embedded Servers: Tomcat, Jetty, Undertow


Actuator: Production monitoring and management

Spring Boot CLI: Command-line interface


Spring Initializr: Project generation tool

23. Client-side load balancing in Spring Boot


Uses Netflix Ribbon or Spring Cloud LoadBalancer:

java

@RestController
public class ClientController {

@Autowired
@LoadBalanced
private RestTemplate restTemplate;

@GetMapping("/call-service")
public String callService() {
return restTemplate.getForObject("http://my-service/api/data", String.class);
}
}

24. How auto-configuration works


Spring Boot auto-configuration uses:

@Conditional annotations: Conditional bean creation

spring.factories: Auto-configuration classes listing


@ConfigurationProperties: External configuration binding

Classpath scanning: Automatic dependency detection


25. @Bean annotation

Creates and configures beans manually:

java

@Configuration
public class AppConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

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

26. @Autowired annotation


Provides automatic dependency injection:

java

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

// Constructor injection (preferred)


public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}

27. Exception handling in Spring Boot


java

@ControllerAdvice
public class CustomExceptionHandler {

@ExceptionHandler(DataAccessException.class)
public ResponseEntity<ErrorResponse> handleDatabaseError(DataAccessException ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("DATABASE_ERROR", ex.getMessage()));
}
}

28. Spring Boot project flow


1. main() method calls SpringApplication.run()
2. SpringApplication creates ApplicationContext

3. Auto-configuration scans classpath and configures beans


4. Component scanning discovers and registers components

5. Embedded server starts (Tomcat by default)

6. Application is ready to serve requests

29. Profiles in Spring Boot

properties

# application-dev.properties
spring.datasource.url=jdbc:h2:mem:devdb

# application-prod.properties
spring.datasource.url=jdbc:mysql://prod-server:3306/proddb

java

@Profile("dev")
@Configuration
public class DevConfig { }

@Profile("prod")
@Configuration
public class ProdConfig { }

Activate profile:
bash

java -jar app.jar --spring.profiles.active=prod

30. Spring Boot context path

properties

server.servlet.context-path=/api

All endpoints will be prefixed with /api .

31. HTTP methods in Spring Boot


GET: Retrieve data (@GetMapping)
POST: Create data (@PostMapping)

PUT: Update entire resource (@PutMapping)


PATCH: Partial update (@PatchMapping)

DELETE: Remove resource (@DeleteMapping)

32. Repository vs DAO layer


Repository focuses on domain objects and business logic:

java

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
List<User> findByActiveTrue();
}

DAO focuses on data access operations:


java

@Component
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;

public User findById(Long id) {


return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new UserRowMapper(), id);
}
}

33. Basic authentication in Spring Boot

java

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated())
.httpBasic();
return http.build();
}
}

34. @PathVariable vs @RequestParam

java

// Path Variable: /users/123


@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) { }

// Request Param: /users?id=123


@GetMapping("/users")
public User getUser(@RequestParam Long id) { }

35. Log4j configuration


properties

# application.properties
logging.level.com.example=DEBUG
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
logging.file.name=app.log

36. @Enable vs @SpringBootApplication


@Enable annotations: Enable specific functionality (@EnableScheduling, @EnableCaching)
@SpringBootApplication: Composite annotation that includes @EnableAutoConfiguration

37. Actuators in Spring Boot


Provides production-ready features:

xml

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

properties

management.endpoints.web.exposure.include=health,info,metrics
management.endpoint.health.show-details=always

Endpoints: /actuator/health , /actuator/info , /actuator/metrics

38. Spring Boot Starters


Pre-configured dependency sets:

spring-boot-starter-web: Web applications


spring-boot-starter-data-jpa: JPA with Hibernate

spring-boot-starter-security: Security features

spring-boot-starter-test: Testing framework

39. HTTP vs HTTPS


HTTP HTTPS

Port 80 Port 443

Unencrypted Encrypted (SSL/TLS)

Fast Slightly slower

Insecure Secure
 

40. Creating Maven project to Spring Boot


1. Add Spring Boot parent:

xml

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
</parent>

2. Add starter dependencies:

xml

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

41. Spring Boot vs Spring MVC


Spring Boot Spring MVC

Auto-configuration Manual configuration

Embedded server External server needed

Minimal setup More setup required

Production-ready Requires additional setup


 

42. Dependency Injection


Three types:

1. Constructor Injection (recommended):


java

@Service
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {


this.userRepository = userRepository;
}
}

2. Setter Injection:

java

@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}

3. Field Injection:

java

@Autowired
private UserRepository userRepository;

43. Spring Boot Actuator


Monitoring and management features:

java

@Component
public class CustomHealthIndicator implements HealthIndicator {

@Override
public Health health() {
// Custom health check logic
return Health.up()
.withDetail("database", "available")
.build();
}
}

44. Spring Boot Initializer


Web-based tool (start.spring.io) to generate Spring Boot projects with:

Dependencies selection

Project metadata
Build tool choice (Maven/Gradle)

Java version selection

45. Creating Spring Bean

java

@Configuration
public class AppConfig {

@Bean
@Scope("singleton") // Default scope
public UserService userService() {
return new UserService();
}

@Bean
@Scope("prototype")
public ShoppingCart shoppingCart() {
return new ShoppingCart();
}
}

46. Repository vs Controller


Repository Controller

Data access layer Presentation layer

Database operations HTTP request handling

CRUD operations Request routing

Data persistence Response formatting


 

47. Excluding Spring Configuration

java

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class MyApplication { }

@ComponentScan(excludeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classe


public class MyApplication { }

 
48. CRUD vs JPA Repositories
CrudRepository:

java

public interface UserRepository extends CrudRepository<User, Long> {


// Basic CRUD operations: save, findById, findAll, delete
}

JpaRepository:

java

public interface UserRepository extends JpaRepository<User, Long> {


// Extends CrudRepository + PagingAndSortingRepository
// Additional methods: flush, saveAndFlush, deleteInBatch
}

49. @RequestBody and @ResponseBody

java

@PostMapping("/users")
public User createUser(@RequestBody User user) { // JSON to Object
return userService.save(user); // Object to JSON (with @RestController)
}

@Controller
public class WebController {
@PostMapping("/users")
@ResponseBody // Explicitly needed with @Controller
public User createUser(@RequestBody User user) {
return userService.save(user);
}
}

50. Types of Controllers


1. @Controller: Traditional MVC controller

2. @RestController: RESTful web services

3. @ControllerAdvice: Global exception handling

51. GET method with @ResponseBody


Yes, @ResponseBody can be used with GET methods:
java

@Controller
public class DataController {

@GetMapping("/data")
@ResponseBody
public List<Data> getData() {
return dataService.findAll(); // Returns JSON
}
}

52. HTTP Methods in detail


GET: Retrieve data (idempotent, safe)
POST: Create new resource (not idempotent)

PUT: Update entire resource (idempotent)


PATCH: Partial update (not necessarily idempotent)
DELETE: Remove resource (idempotent)

53. Spring Boot Architecture

Presentation Layer (Controllers)



Business Layer (Services)

Persistence Layer (Repositories)

Database Layer

54. @ComponentScan
Configures component scanning:

java

@ComponentScan(basePackages = {"com.example.service", "com.example.repository"})


@SpringBootApplication
public class MyApplication { }

55. @Service annotation


Marks service layer components:
java

@Service
@Transactional
public class UserService {

@Autowired
private UserRepository userRepository;

public User createUser(User user) {


// Business logic here
return userRepository.save(user);
}
}

56. @Repository annotation


Marks data access components:

java

@Repository
public class CustomUserRepository {

@Autowired
private EntityManager entityManager;

public List<User> findActiveUsers() {


return entityManager.createQuery(
"SELECT u FROM User u WHERE u.active = true", User.class)
.getResultList();
}
}

57. JPA (Java Persistence API)


Specification for ORM in Java:
java

@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

@Column(unique = true)
private String email;

// Getters and setters


}

58. Sending mail in Spring Boot

xml

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

java

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

public void sendEmail(String to, String subject, String body) {


SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(body);
mailSender.send(message);
}
}

59. Steps to design REST API


1. Identify Resources: Users, Orders, Products
2. Define URIs: /api/users, /api/orders

3. Choose HTTP Methods: GET, POST, PUT, DELETE


4. Design Request/Response: JSON format

5. Handle Errors: Proper HTTP status codes


6. Add Security: Authentication/Authorization
7. Documentation: API documentation

8. Testing: Unit and integration tests

60. HTTP Status Codes


200 OK: Request successful

201 Created: Resource created successfully


400 Bad Request: Invalid request
401 Unauthorized: Authentication required

403 Forbidden: Access denied


404 Not Found: Resource not found

500 Internal Server Error: Server error

Design Patterns Questions

61. What is Factory Design Pattern?


Creational pattern that creates objects without specifying exact classes:
java

// Product interface
interface Vehicle {
void start();
}

// Concrete products
class Car implements Vehicle {
public void start() { System.out.println("Car started"); }
}

class Bike implements Vehicle {


public void start() { System.out.println("Bike started"); }
}

// Factory
class VehicleFactory {
public static Vehicle createVehicle(String type) {
switch(type.toLowerCase()) {
case "car": return new Car();
case "bike": return new Bike();
default: throw new IllegalArgumentException("Unknown vehicle type");
}
}
}

// Usage
Vehicle car = VehicleFactory.createVehicle("car");
car.start();

62. Singleton Design Pattern


Ensures only one instance of a class exists:
java

public class DatabaseConnection {


private static DatabaseConnection instance;
private static final Object lock = new Object();

private DatabaseConnection() {} // Private constructor

// Thread-safe singleton
public static DatabaseConnection getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new DatabaseConnection();
}
}
}
return instance;
}

public void connect() {


System.out.println("Database connected");
}
}

// Usage
DatabaseConnection db = DatabaseConnection.getInstance();
db.connect();

63. Why use Factory Design Pattern?


Loose Coupling: Client doesn't need to know concrete classes
Code Reusability: Centralized object creation
Easy Maintenance: Changes in one place

Follows OCP: Open for extension, closed for modification

64. Abstract Factory Design Pattern


Creates families of related objects:
java

// Abstract Factory
interface GUIFactory {
Button createButton();
TextField createTextField();
}

// Concrete Factories
class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
public TextField createTextField() { return new WindowsTextField(); }
}

class MacFactory implements GUIFactory {


public Button createButton() { return new MacButton(); }
public TextField createTextField() { return new MacTextField(); }
}

// Products
interface Button { void render(); }
interface TextField { void render(); }

class WindowsButton implements Button {


public void render() { System.out.println("Windows Button"); }
}

class MacButton implements Button {


public void render() { System.out.println("Mac Button"); }
}

65. Observer Design Pattern


Defines one-to-many dependency between objects:
java

import java.util.*;

// Subject
class NewsAgency {
private List<Observer> observers = new ArrayList<>();
private String news;

public void addObserver(Observer observer) {


observers.add(observer);
}

public void removeObserver(Observer observer) {


observers.remove(observer);
}

public void setNews(String news) {


this.news = news;
notifyObservers();
}

private void notifyObservers() {


for (Observer observer : observers) {
observer.update(news);
}
}
}

// Observer
interface Observer {
void update(String news);
}

class NewsChannel implements Observer {


private String name;

public NewsChannel(String name) {


this.name = name;
}

public void update(String news) {


System.out.println(name + " received news: " + news);
}
}
66. Builder Design Pattern
Constructs complex objects step by step:
java
public class Computer {
private String CPU;
private String RAM;
private String storage;
private String GPU;

private Computer(Builder builder) {


this.CPU = builder.CPU;
this.RAM = builder.RAM;
this.storage = builder.storage;
this.GPU = builder.GPU;
}

public static class Builder {


private String CPU;
private String RAM;
private String storage;
private String GPU;

public Builder setCPU(String CPU) {


this.CPU = CPU;
return this;
}

public Builder setRAM(String RAM) {


this.RAM = RAM;
return this;
}

public Builder setStorage(String storage) {


this.storage = storage;
return this;
}

public Builder setGPU(String GPU) {


this.GPU = GPU;
return this;
}

public Computer build() {


return new Computer(this);
}
}
}

// Usage
Computer computer = new Computer.Builder()
.setCPU("Intel i7")
.setRAM("16GB")
.setStorage("512GB SSD")
.setGPU("NVIDIA RTX 3080")
.build();

67. Adapter Design Pattern


Allows incompatible interfaces to work together:
java

// Target interface
interface MediaPlayer {
void play(String audioType, String fileName);
}

// Adaptee
class AdvancedMediaPlayer {
public void playVlc(String fileName) {
System.out.println("Playing vlc file: " + fileName);
}

public void playMp4(String fileName) {


System.out.println("Playing mp4 file: " + fileName);
}
}

// Adapter
class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedPlayer;

public MediaAdapter(String audioType) {


if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
advancedPlayer = new AdvancedMediaPlayer();
}
}

public void play(String audioType, String fileName) {


if (audioType.equalsIgnoreCase("vlc")) {
advancedPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedPlayer.playMp4(fileName);
}
}
}

68. Breaking Singleton Pattern


Ways to break singleton:

1. Reflection:
java

Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();


constructor.setAccessible(true);
Singleton instance = constructor.newInstance();

2. Serialization:

java

// Prevent by implementing readResolve()


private Object readResolve() {
return getInstance();
}

3. Cloning:

java

// Prevent by overriding clone()


@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

69. Why private constructor in Singleton?


Prevents external instantiation
Ensures single instance creation

Forces use of getInstance() method


Maintains singleton contract

70. Design Patterns Categories


1. Creational: Object creation (Singleton, Factory, Builder)

2. Structural: Object composition (Adapter, Decorator, Facade)


3. Behavioral: Object interaction (Observer, Strategy, Command)

Additional Advanced Topics

Spring Security Integration


java

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.oauth2Login()
.jwt();
return http.build();
}
}

Custom Validation
java

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EmailValidator.class)
public @interface ValidEmail {
String message() default "Invalid email format";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

public class EmailValidator implements ConstraintValidator<ValidEmail, String> {


private static final String EMAIL_PATTERN = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+$";

@Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return email != null && email.matches(EMAIL_PATTERN);
}
}

// Usage
public class User {
@ValidEmail
private String email;
}

Testing in Spring Boot


java
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserServiceTest {

@Autowired
private UserService userService;

@MockBean
private UserRepository userRepository;

@Test
void testCreateUser() {
// Given
User user = new User("John", "[email protected]");
when(userRepository.save(any(User.class))).thenReturn(user);

// When
User result = userService.createUser(user);

// Then
assertThat(result.getName()).isEqualTo("John");
verify(userRepository).save(user);
}
}

@WebMvcTest(UserController.class)
class UserControllerTest {

@Autowired
private MockMvc mockMvc;

@MockBean
private UserService userService;

@Test
void testGetUser() throws Exception {
User user = new User("John", "[email protected]");
when(userService.findById(1L)).thenReturn(user);

mockMvc.perform(get("/api/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("John"))
.andExpect(jsonPath("$.email").value("[email protected]"));
}
}
Caching in Spring Boot

java

@Configuration
@EnableCaching
public class CacheConfig {

@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users", "products");
}
}

@Service
public class UserService {

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


public User findById(Long id) {
// Expensive database operation
return userRepository.findById(id).orElse(null);
}

@CacheEvict(value = "users", key = "#user.id")


public User updateUser(User user) {
return userRepository.save(user);
}

@CacheEvict(value = "users", allEntries = true)


public void clearCache() {
// Clear all cache entries
}
}

Async Processing
java

@Configuration
@EnableAsync
public class AsyncConfig {

@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}

@Service
public class EmailService {

@Async
public CompletableFuture<String> sendEmailAsync(String to, String subject) {
// Simulate email sending
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return CompletableFuture.completedFuture("Email sent to " + to);
}
}

Event Handling
java

// Custom Event
public class UserRegisteredEvent extends ApplicationEvent {
private final User user;

public UserRegisteredEvent(Object source, User user) {


super(source);
this.user = user;
}

public User getUser() {


return user;
}
}

// Event Publisher
@Service
public class UserService {

@Autowired
private ApplicationEventPublisher eventPublisher;

public User registerUser(User user) {


User savedUser = userRepository.save(user);

// Publish event
eventPublisher.publishEvent(new UserRegisteredEvent(this, savedUser));

return savedUser;
}
}

// Event Listener
@Component
public class UserEventListener {

@EventListener
@Async
public void handleUserRegistered(UserRegisteredEvent event) {
User user = event.getUser();
// Send welcome email
emailService.sendWelcomeEmail(user.getEmail());
}
}
Custom Health Indicators

java

@Component
public class DatabaseHealthIndicator implements HealthIndicator {

@Autowired
private DataSource dataSource;

@Override
public Health health() {
try (Connection connection = dataSource.getConnection()) {
if (connection.isValid(1)) {
return Health.up()
.withDetail("database", "Available")
.withDetail("validationQuery", "SELECT 1")
.build();
}
} catch (SQLException e) {
return Health.down()
.withDetail("database", "Unavailable")
.withDetail("error", e.getMessage())
.build();
}
return Health.down().withDetail("database", "Connection failed").build();
}
}

Transaction Management
java

@Service
@Transactional
public class BankService {

@Autowired
private AccountRepository accountRepository;

@Transactional(propagation = Propagation.REQUIRED)
public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) {
Account fromAccount = accountRepository.findById(fromAccountId)
.orElseThrow(() -> new AccountNotFoundException("From account not found"));

Account toAccount = accountRepository.findById(toAccountId)


.orElseThrow(() -> new AccountNotFoundException("To account not found"));

if (fromAccount.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException("Insufficient balance");
}

fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
toAccount.setBalance(toAccount.getBalance().add(amount));

accountRepository.save(fromAccount);
accountRepository.save(toAccount);
}

@Transactional(readOnly = true)
public List<Account> getAllAccounts() {
return accountRepository.findAll();
}
}

Custom Annotations
java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
String value() default "";
}

@Aspect
@Component
public class LoggingAspect {

@Around("@annotation(logExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint, LogExecutionTime logExecution
long start = System.currentTimeMillis();

Object result = joinPoint.proceed();

long executionTime = System.currentTimeMillis() - start;

String methodName = joinPoint.getSignature().getName();


String logMessage = logExecutionTime.value().isEmpty() ? methodName : logExecutionTime.

System.out.println(logMessage + " executed in " + executionTime + " ms");

return result;
}
}

// Usage
@Service
public class UserService {

@LogExecutionTime("User creation")
public User createUser(User user) {
return userRepository.save(user);
}
}

 

Microservices Communication
java

// Feign Client
@FeignClient(name = "user-service", url = "http://localhost:8081")
public interface UserServiceClient {

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

@PostMapping("/users")
User createUser(@RequestBody User user);
}

// Circuit Breaker with Resilience4j


@Service
public class OrderService {

@Autowired
private UserServiceClient userServiceClient;

@CircuitBreaker(name = "user-service", fallbackMethod = "fallbackUser")


@Retry(name = "user-service")
@TimeLimiter(name = "user-service")
public CompletableFuture<User> getUserWithCircuitBreaker(Long userId) {
return CompletableFuture.supplyAsync(() -> userServiceClient.getUserById(userId));
}

public CompletableFuture<User> fallbackUser(Long userId, Exception ex) {


return CompletableFuture.completedFuture(new User("Unknown", "[email protected]"));
}
}

Advanced JPA Features


java
@Entity
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {

@CreatedDate
@Column(name = "created_date", updatable = false)
private LocalDateTime createdDate;

@LastModifiedDate
@Column(name = "last_modified_date")
private LocalDateTime lastModifiedDate;

@CreatedBy
@Column(name = "created_by", updatable = false)
private String createdBy;

@LastModifiedBy
@Column(name = "last_modified_by")
private String lastModifiedBy;
}

@Entity
public class User extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String name;

@Column(unique = true)
private String email;

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)


private List<Order> orders = new ArrayList<>();

@ManyToMany
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
}
// Custom Repository Implementation
@Repository
public class CustomUserRepositoryImpl implements CustomUserRepository {

@PersistenceContext
private EntityManager entityManager;

@Override
public List<User> findUsersWithCustomCriteria(String name, String email) {
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);

List<Predicate> predicates = new ArrayList<>();

if (name != null && !name.isEmpty()) {


predicates.add(cb.like(user.get("name"), "%" + name + "%"));
}

if (email != null && !email.isEmpty()) {


predicates.add(cb.equal(user.get("email"), email));
}

query.where(predicates.toArray(new Predicate[0]));

return entityManager.createQuery(query).getResultList();
}
}

Configuration Properties
java

@ConfigurationProperties(prefix = "app")
@Component
public class AppProperties {

private String name;


private String version;
private Security security = new Security();
private Database database = new Database();

public static class Security {


private String jwtSecret;
private int jwtExpirationMs;

// Getters and setters


}

public static class Database {


private String url;
private String username;
private String password;
private int maxConnections;

// Getters and setters


}

// Getters and setters


}

// application.yml
app:
name: MyApplication
version: 1.0.0
security:
jwt-secret: mySecretKey
jwt-expiration-ms: 86400000
database:
url: jdbc:mysql://localhost:3306/mydb
username: user
password: password
max-connections: 20

Monitoring and Metrics


java

@Component
public class CustomMetrics {

private final Counter userCreationCounter;


private final Timer userCreationTimer;
private final Gauge activeUsersGauge;

public CustomMetrics(MeterRegistry meterRegistry) {


this.userCreationCounter = Counter.builder("user.creation.count")
.description("Number of users created")
.register(meterRegistry);

this.userCreationTimer = Timer.builder("user.creation.time")
.description("Time taken to create user")
.register(meterRegistry);

this.activeUsersGauge = Gauge.builder("user.active.count")
.description("Number of active users")
.register(meterRegistry, this, CustomMetrics::getActiveUserCount);
}

public void incrementUserCreation() {


userCreationCounter.increment();
}

public void recordUserCreationTime(Duration duration) {


userCreationTimer.record(duration);
}

private double getActiveUserCount() {


// Logic to count active users
return 100.0; // Example value
}
}

WebSocket Configuration
java

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new ChatWebSocketHandler(), "/chat")
.setAllowedOrigins("*");
}
}

@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {

private final Set<WebSocketSession> sessions = new CopyOnWriteArraySet<>();

@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
System.out.println("Connection established: " + session.getId());
}

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exce
// Broadcast message to all connected clients
for (WebSocketSession webSocketSession : sessions) {
if (webSocketSession.isOpen()) {
webSocketSession.sendMessage(message);
}
}
}

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
System.out.println("Connection closed: " + session.getId());
}
}

 

Scheduling
java

@Configuration
@EnableScheduling
public class SchedulingConfig {

@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(10);
scheduler.setThreadNamePrefix("scheduled-");
scheduler.initialize();
return scheduler;
}
}

@Component
public class ScheduledTasks {

@Scheduled(fixedRate = 60000) // Every minute


public void performPeriodicTask() {
System.out.println("Periodic task executed at: " + LocalDateTime.now());
}

@Scheduled(cron = "0 0 2 * * ?") // Every day at 2 AM


public void performDailyCleanup() {
System.out.println("Daily cleanup executed at: " + LocalDateTime.now());
}

@Scheduled(fixedDelay = 30000, initialDelay = 10000) // 30 seconds after previous completio


public void performDelayedTask() {
System.out.println("Delayed task executed at: " + LocalDateTime.now());
}
}

 

Advanced Design Patterns

Command Pattern
java
// Command interface
interface Command {
void execute();
void undo();
}

// Concrete commands
class CreateUserCommand implements Command {
private UserService userService;
private User user;
private User createdUser;

public CreateUserCommand(UserService userService, User user) {


this.userService = userService;
this.user = user;
}

@Override
public void execute() {
createdUser = userService.createUser(user);
}

@Override
public void undo() {
if (createdUser != null) {
userService.deleteUser(createdUser.getId());
}
}
}

// Invoker
class CommandInvoker {
private Stack<Command> commandHistory = new Stack<>();

public void executeCommand(Command command) {


command.execute();
commandHistory.push(command);
}

public void undoLastCommand() {


if (!commandHistory.isEmpty()) {
Command lastCommand = commandHistory.pop();
lastCommand.undo();
}
}
}

Strategy Pattern
java

// Strategy interface
interface PaymentStrategy {
void pay(double amount);
}

// Concrete strategies
class CreditCardPayment implements PaymentStrategy {
private String cardNumber;

public CreditCardPayment(String cardNumber) {


this.cardNumber = cardNumber;
}

@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using Credit Card: " + cardNumber);
}
}

class PayPalPayment implements PaymentStrategy {


private String email;

public PayPalPayment(String email) {


this.email = email;
}

@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using PayPal: " + email);
}
}

// Context
class PaymentContext {
private PaymentStrategy paymentStrategy;

public void setPaymentStrategy(PaymentStrategy paymentStrategy) {


this.paymentStrategy = paymentStrategy;
}

public void executePayment(double amount) {


paymentStrategy.pay(amount);
}
}
Template Method Pattern
java
abstract class DataProcessor {

// Template method
public final void processData() {
readData();
processData();
writeData();
}

protected abstract void readData();


protected abstract void processData();
protected abstract void writeData();
}

class CSVDataProcessor extends DataProcessor {

@Override
protected void readData() {
System.out.println("Reading CSV data");
}

@Override
protected void processData() {
System.out.println("Processing CSV data");
}

@Override
protected void writeData() {
System.out.println("Writing CSV data");
}
}

class XMLDataProcessor extends DataProcessor {

@Override
protected void readData() {
System.out.println("Reading XML data");
}

@Override
protected void processData() {
System.out.println("Processing XML data");
}

@Override
protected void writeData() {
System.out.println("Writing XML data");
}
}

Decorator Pattern
java
// Component interface
interface Coffee {
String getDescription();
double getCost();
}

// Concrete component
class SimpleCoffee implements Coffee {
@Override
public String getDescription() {
return "Simple Coffee";
}

@Override
public double getCost() {
return 2.0;
}
}

// Base decorator
abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;

public CoffeeDecorator(Coffee coffee) {


this.coffee = coffee;
}

@Override
public String getDescription() {
return coffee.getDescription();
}

@Override
public double getCost() {
return coffee.getCost();
}
}

// Concrete decorators
class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return coffee.getDescription() + ", Milk";
}

@Override
public double getCost() {
return coffee.getCost() + 0.5;
}
}

class SugarDecorator extends CoffeeDecorator {


public SugarDecorator(Coffee coffee) {
super(coffee);
}

@Override
public String getDescription() {
return coffee.getDescription() + ", Sugar";
}

@Override
public double getCost() {
return coffee.getCost() + 0.2;
}
}

// Usage
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDescription() + " costs $" + coffee.getCost());

Facade Pattern
java

// Subsystem classes
class CPU {
public void freeze() { System.out.println("CPU freezing"); }
public void jump(long position) { System.out.println("CPU jumping to " + position); }
public void execute() { System.out.println("CPU executing"); }
}

class Memory {
public void load(long position, byte[] data) {
System.out.println("Memory loading data at position " + position);
}
}

class HardDrive {
public byte[] read(long lba, int size) {
System.out.println("Hard drive reading " + size + " bytes from " + lba);
return new byte[size];
}
}

// Facade
class ComputerFacade {
private CPU cpu;
private Memory memory;
private HardDrive hardDrive;

public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.hardDrive = new HardDrive();
}

public void start() {


cpu.freeze();
memory.load(0, hardDrive.read(0, 1024));
cpu.jump(0);
cpu.execute();
System.out.println("Computer started successfully");
}
}

// Usage
ComputerFacade computer = new ComputerFacade();
computer.start();
Best Practices and Tips

1. Spring Boot Best Practices


Use constructor injection over field injection
Keep controllers thin, move business logic to services

Use appropriate HTTP status codes


Implement proper exception handling

Use profiles for different environments


Write comprehensive tests
Use caching strategically

Monitor application performance

2. Design Pattern Best Practices


Choose patterns based on actual needs, not just because they exist

Keep it simple - don't over-engineer


Document pattern usage and reasoning
Consider performance implications

Test pattern implementations thoroughly


Be consistent in pattern usage across the application

3. Interview Tips
Understand the problem before suggesting a solution
Be able to explain trade-offs and alternatives

Practice coding examples by hand


Prepare to discuss real-world scenarios
Know when NOT to use certain patterns or features

Be ready to defend your architectural choices


Stay updated with latest Spring Boot versions and features

This comprehensive guide covers all the major Spring Boot and Design Pattern questions commonly asked
in interviews. Practice these concepts with hands-on coding to build confidence and deep understanding.

You might also like