Spring fundamentals interview questions
-
Last Updated: April 5, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn Spring fundamentals interview questions with clear answers. Covers IoC, Dependency Injection, beans, lifecycle, and core concepts in Spring.
Ans: Spring Framework is a lightweight, modular, and comprehensive framework for building Java applications. It was designed to make enterprise Java development simpler, cleaner, and easier to manage.
Before Spring, building Java applications involved a lot of manual work. Developers had to create objects using new, wire dependencies manually, manage transactions with boilerplate code, and handle configuration in a scattered way. This often led to tightly coupled code that was difficult to maintain, test, and extend.
Spring solves these problems by introducing a few key concepts.
First, it manages object creation and wiring. Instead of manually creating and connecting objects:
OrderService orderService = new OrderService(); PaymentService paymentService = new PaymentService(); orderService.setPaymentService(paymentService);
Spring automatically creates and injects dependencies, reducing manual effort.
Second, it promotes loose coupling. Instead of classes creating their own dependencies:
public class OrderService {
private PaymentService paymentService = new PaymentService();
}
Spring encourages dependency injection:
public class OrderService {
private PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
This makes the application more flexible, maintainable, and testable.
Spring also reduces boilerplate code by handling common concerns such as transactions, logging, and security through features like declarative transaction management and AOP.
Another major advantage is improved testability. Since dependencies are injected, they can be easily replaced with mocks during testing.
Finally, Spring provides a complete ecosystem for building applications, including support for web development, database access, transactions, security, and more, all in a consistent way.
In simple terms, Spring solves the problem of complex, tightly coupled Java applications by making them loosely coupled, easier to maintain, and easier to test.
Ans: Spring Framework is modular, which means it is divided into multiple modules, each responsible for a specific functionality. You do not need to use all modules — you can include only those required for your application.
The major core modules are as follows:
a. Core Container
This is the heart of the Spring Framework. It provides fundamental features such as Inversion of Control (IoC) and Dependency Injection (DI).
It mainly includes:
spring-corespring-beansspring-contextspring-expressionThis module is responsible for creating, configuring, and managing Spring beans.
b. AOP (Aspect-Oriented Programming) Module
This module helps in separating cross-cutting concerns such as logging, security, transaction handling, and auditing.
Instead of writing the same code in multiple places, AOP allows you to apply such logic in a centralized and cleaner way.
c. Data Access / Integration Module
This module simplifies working with databases and integration technologies.
It provides support for:
Spring reduces boilerplate code by handling tasks like connection management, exception handling, and resource cleanup.
d. Transaction Module
This module provides both declarative and programmatic transaction management.
Instead of manually managing transactions, you can use annotations like:
@Transactional
public void transferMoney() {
// business logic
}
Spring automatically handles transaction boundaries such as commit and rollback.
e. Web Module
This module is used for building web applications.
It includes:
spring-webspring-webmvcSpring MVC is part of this module and helps build applications using concepts like controllers, models, views, and request mapping.
f. Test Module
Spring also provides strong testing support.
It helps in:
In summary, Spring’s modular architecture allows developers to use only the features they need, making applications more flexible, lightweight, and easier to manage.
Ans: Inversion of Control (IoC) is a core concept in Spring, in which control over object creation and dependency management is shifted from application code to the Spring container.
In traditional Java programming, a class creates its own dependencies:
public class Car {
private Engine engine = new Engine();
}
Here, the Car class controls the creation of Engine, leading to tight coupling.
With IoC, this control is inverted. The class does not create dependencies; instead, they are provided from outside:
public class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
Now, the responsibility of creating and injecting Engine is handled by the Spring container.
IoC is a design principle in which the framework manages object creation, dependency wiring, and the object lifecycle. Dependency Injection (DI) is the mechanism through which Spring implements IoC.
The main benefit of IoC is that it makes applications loosely coupled, easier to maintain, and easier to test.
Ans: Dependency Injection (DI) is the mechanism that Spring uses to implement Inversion of Control. It means that instead of a class creating its dependencies, those dependencies are provided to it from outside.
A dependency is simply an object that a class needs to perform its work. For example, if OrderService needs PaymentService, then PaymentService is its dependency.
Without DI:
public class OrderService {
private PaymentService paymentService = new PaymentService();
}
This creates tight coupling and makes testing difficult.
With DI:
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
}
Now the dependency is injected from outside, typically by the Spring container.
DI improves:
In simple terms, DI allows classes to focus only on their logic while Spring handles dependency management.
Ans: Spring supports three main types of dependency injection: constructor injection, setter injection, and field injection.
a. Constructor Injection (Recommended)
Dependencies are provided through the constructor at the time of object creation:
public class EmployeeService {
private final EmployeeRepository employeeRepository;
public EmployeeService(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
}
This makes dependencies mandatory and explicit, and promotes immutability.
b. Setter Injection
Dependencies are set using setter methods after object creation:
public class EmployeeService {
private EmployeeRepository employeeRepository;
public void setEmployeeRepository(EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
}
This is useful when dependencies are optional, but the object may exist in an incomplete state.
c. Field Injection
Dependencies are injected directly into fields using annotations:
public class EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
}
This is concise but generally not recommended in production because it hides dependencies and makes testing harder.
In modern Spring applications, constructor injection is preferred because it ensures better design, immutability, and testability.
Ans: In most real-world applications, constructor injection is preferred over setter injection because it ensures that objects are created in a complete and valid state.
With constructor injection, dependencies are provided at the time of object creation:
public class PaymentService {
private final BankClient bankClient;
public PaymentService(BankClient bankClient) {
this.bankClient = bankClient;
}
}
This makes dependencies mandatory and explicit, and also allows fields to be final, improving immutability and safety.
With setter injection, dependencies are set after object creation:
public class PaymentService {
private BankClient bankClient;
public void setBankClient(BankClient bankClient) {
this.bankClient = bankClient;
}
}
This means the object can exist without required dependencies, leading to partially initialized objects and possible runtime errors.
Constructor injection also makes testing easier, as dependencies can be directly passed during object creation.
Setter injection is still useful when dependencies are optional or need to be changed later, but for required dependencies, constructor injection is the better choice.
Ans: ApplicationContext is the central interface of the Spring container. It represents a fully functional IoC container responsible for creating beans, injecting dependencies, managing lifecycle, and providing additional enterprise features.
When a Spring application starts, the ApplicationContext is initialized. It reads the configuration, creates beans, wires dependencies, and makes them available for use.
For example:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); UserService userService = context.getBean(UserService.class);
Beyond basic bean management, ApplicationContext provides features like:
In simple terms, ApplicationContext acts as the runtime environment of a Spring application, managing all beans and their interactions.
Ans: Both BeanFactory and ApplicationContext are Spring containers, but ApplicationContext is more advanced and commonly used in real-world applications.
BeanFactory is the basic container that provides core functionality for creating and managing beans. It supports lazy initialization, meaning beans are created only when requested.
ApplicationContext extends BeanFactory and adds enterprise-level features such as:
Another key difference is bean initialization. ApplicationContext typically creates singleton beans eagerly at startup, which helps detect configuration issues early.
In simple terms, BeanFactory is the basic container, while ApplicationContext is the full-featured, production-ready container used in most Spring applications.
Ans: A Spring bean is simply an object that is created, managed, and stored by the Spring container.
Not every Java object is a Spring bean. For example:
UserService service = new UserService();
This is just a normal object — Spring does not manage it.
But when a class is registered with Spring using annotations like @Component, @Service, @Repository, or @Bean, Spring creates and manages it:
@Service
public class UserService {
}
Now UserService becomes a Spring bean.
What makes beans special is that Spring controls:
By default, beans are singletons, but other scopes like prototype, request, and session are also supported.
In simple terms, a Spring bean is any object whose lifecycle and dependencies are managed by the Spring container.
Ans: The bean lifecycle in Spring represents the complete journey of a bean — from creation to destruction — managed by the Spring container.
A Spring bean is not just created using new. Instead, it goes through multiple stages where Spring injects dependencies, applies processing, and prepares it for use.
Spring first reads metadata about beans (class, scope, dependencies, init/destroy methods) from:
@Component, @Service)@Bean)Important: Spring first understands what to create, then creates the object.
Spring creates the actual object using a constructor:
@Component
public class Engine {
public Engine() {
System.out.println("Engine created");
}
}
At this stage, the object is created but not fully initialized.
Spring injects required dependencies:
@Component
public class Car {
private final Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
}
Now the bean has all the required collaborators.
If the bean implements Aware interfaces (like BeanNameAware, ApplicationContextAware), Spring provides container-related information.
Example:
@Component
public class OrderService implements BeanNameAware {
@Override
public void setBeanName(String name) {
System.out.println("Bean Name: " + name);
}
}
Spring allows modification of the bean before initialization using:
postProcessBeforeInitialization()
At this stage, you can:
Spring calls initialization logic after dependencies are injected.
Common ways:
@PostConstructInitializingBeaninitMethodExample:
@PostConstruct
public void init() {
System.out.println("Bean initialized");
}
Spring again processes the bean:
postProcessAfterInitialization()
This is where proxies are created.
For example:
@TransactionalSpring wraps the original bean with a proxy and stores that in the container.
Now the bean is fully initialized and available for use via:
context.getBean(MyService.class);
For singleton beans, Spring stores them in a cache.
When the container shuts down, Spring calls the destroy methods:
@PreDestroyDisposableBeanA Spring bean goes through multiple phases — creation → injection → processing → initialization → proxying → usage → destruction.
This lifecycle is what enables Spring features like:
Ans: Internally, Spring manages the bean lifecycle using a set of core container classes and multiple levels of processing rather than simple object creation.
At the center of this process is the BeanDefinition, which acts as a blueprint for every bean. Spring first converts configuration (annotations, XML, Java config) into BeanDefinition objects and stores them inside the container.
The actual lifecycle processing is handled mainly by the DefaultListableBeanFactory class and its parent class AbstractAutowireCapableBeanFactory. These classes are responsible for creating, wiring, and initializing beans.
When a bean is requested, Spring calls an internal method like:
getBean()
This triggers a chain of internal methods, such as:
createBean()doCreateBean()populateBean()initializeBean()Each of these methods handles a specific part of the lifecycle.
Spring uses three levels of cache to manage singleton beans and handle circular dependencies:
This mechanism allows Spring to resolve circular dependencies safely.
During populateBean():
@AutowiredThis step is handled by internal components like:
AutowiredAnnotationBeanPostProcessorSpring maintains a list of BeanPostProcessors and applies them in order:
postProcessBeforeInitialization()postProcessAfterInitialization()This is where major features happen:
@TransactionalIf a bean requires additional behavior (such as transactions), Spring does NOT modify the original object.
Instead, it creates a proxy using:
The proxy wraps the original bean and adds extra logic.
Spring internally detects and executes:
@PostConstructInitializingBeanSimilarly, during shutdown:
@PreDestroyDisposableBeanAfter all processing:
getBean()Ans: A BeanPostProcessor is a Spring extension point that allows processing of a bean before and after its initialization. It is one of the most important mechanisms in Spring because many advanced features are built on top of it.
Whenever Spring creates a bean, it gives all registered BeanPostProcessors a chance to work on it. This processing can include:
In simple terms, it acts like a checkpoint where Spring asks:
“Do you want to modify or enhance this bean before I finalize it?”
a. Before Initialization
postProcessBeforeInitialization(Object bean, String beanName)
Runs before initialization callbacks like @PostConstruct.
Use cases:
b. After Initialization
postProcessAfterInitialization(Object bean, String beanName)
Runs after initialization is complete.
This is the most important phase because Spring creates proxies here.
@Service
@Transactional
public class AccountService {
public void transfer() {
System.out.println("Money transfer");
}
}
When you call:
accountService.transfer();
Internally:
This proxy is typically created in postProcessAfterInitialization().
Many Spring features rely on it internally:
@Autowired → dependency injection@PostConstruct → lifecycle callbacks@Transactional → transaction proxy@Async, @CacheableSome common implementations:
AutowiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessorAbstractAutoProxyCreatorSo, annotations in Spring are not magic — they are implemented using BeanPostProcessors.
BeanPostProcessor allows Spring to intercept and enhance beans throughout their lifecycle, enabling powerful features such as AOP, transactions, and dependency injection.
Ans: InitializingBean and DisposableBean are Spring lifecycle callback interfaces that allow a bean to execute custom logic during initialization and destruction.
If a bean implements InitializingBean, Spring calls:
afterPropertiesSet()
after all dependencies are injected.
Example:
public class MyService implements InitializingBean {
@Override
public void afterPropertiesSet() {
System.out.println("Initialization logic executed");
}
}
Used for performing setup tasks after the bean is fully configured.
If a bean implements DisposableBean, Spring calls:
destroy()
When the container is shutting down.
Example:
public class MyService implements DisposableBean {
@Override
public void destroy() {
System.out.println("Cleanup logic executed");
}
}
Used for releasing resources like database connections, threads, or files.
Although these interfaces are important, modern Spring applications prefer:
@PostConstruct (for initialization)@PreDestroy (for cleanup)Because they reduce tight coupling with Spring-specific interfaces.
InitializingBean → initialization logicDisposableBean → cleanup logicUnderstanding Spring fundamentals is crucial for cracking interviews and building strong applications. In this article, we covered key concepts like IoC, Dependency Injection, beans, and the bean lifecycle. These concepts form the foundation of everything you do in Spring.
If you are clear with these topics, you can confidently answer most basic to intermediate Spring interview questions.