π Java Developer Switch Preparation #03: What Spring Is Actually Doing Behind the Scenes
Understanding how Spring discovers, creates, and connects objects β beyond just annotations.
π Why I Wanted to Go One Level Deeper
While preparing for my next Java developer switch, I started revisiting Spring Boot from the basics.
I realized I could use annotations like:
@Service Β· @Autowired Β· @Component
I could build APIs and work with Spring-based projects.
But if someone asked me, βHow does Spring actually create and manage these objects?β, I wasnβt completely confident explaining it.
In my previous blog, I covered Spring fundamentals such as IoC, Dependency Injection, Beans, and ApplicationContext.
This time, I wanted to understand what happens behind the annotations.
π§ 1. The Problem Spring Is Trying to Solve
Letβs imagine we have a simple application:
public class PaymentService {
private final PaymentRepository repository;
public PaymentService() {
this.repository = new PaymentRepository();
}
}It works, but PaymentService is responsible for creating its own dependency.
As an application grows, this can make classes harder to change and test.
Spring helps us separate business logic from object creation and dependency management.
π 2. How Does Spring Discover Our Classes?
Letβs modify our example:
@Repository
public class PaymentRepository {
public void savePayment() {
System.out.println("Payment saved");
}
}@Service
public class PaymentService {
private final PaymentRepository repository;
public PaymentService(PaymentRepository repository) {
this.repository = repository;
}
}Notice that we no longer create PaymentRepository using new inside PaymentService.
Instead, the service declares its dependency through the constructor, and Spring can provide it when creating the service.
But how does Spring know these classes should be managed?
Thatβs where component scanning comes in.
In a typical Spring Boot application, @SpringBootApplication enables component scanning for the package containing the application class and its subpackages by default.
Spring can discover eligible classes annotated with:
@Componentβ a general-purpose Spring-managed component.@Serviceβ typically used for business logic.@Repositoryβ typically used for data-access components.@Controllerβ typically used for Spring MVC controllers.
These are stereotype annotations that help Spring identify components for registration.
For example, in our payment application:
PaymentApplication
|
v
Component Scanning
|
v
PaymentService and PaymentRepository
|
v
Bean Definitions Registered
|
v
Beans Created and Dependencies InjectedOne important distinction: discovering a component and creating its actual object are separate steps.
Spring first registers information about how a bean should be created and configured. It then creates and manages the bean according to the application configuration and lifecycle.
π« 3. What Is the Difference Between a Bean Definition and a Spring Bean?
These terms can sound similar when youβre learning Spring.
A bean definition contains information about how Spring should create and configure an object.
A Spring Bean is the actual object managed by the Spring container.
Think of it like this:
Bean Definition
|
v
Bean Instance
|
v
Managed by SpringThis helped me understand that a Spring Bean isnβt simply any Java object. It is an object registered with and managed by the Spring container.
π 4. @Component vs. @Bean
This was another distinction I wanted to understand instead of simply memorizing.
@Component
We put it on a class that Spring can discover through component scanning.
@Component
public class EmailService {
}Spring can discover the class and register it as a bean.
@Bean
We use @Bean on a method that returns the object Spring should manage.
@Configuration
public class AppConfig {
@Bean
public EmailClient emailClient() {
return new EmailClient();
}
}Imagine EmailClient comes from a third-party library. We don't own its source code, so we may not be able to add @Component to that class.
Instead, we can declare a @Bean method in our configuration class to create the object and register it with Spring.
The same approach can also be used for our own classes when we want explicit control over object creation or configuration.
Get juhie sayyedβs stories inΒ yourΒ inbox
Join Medium for free to get updates fromΒ thisΒ writer.
The key difference:
@Componentβ Spring discovers the class through component scanning.@Beanβ Spring registers the object returned by a configuration method.
π 5. What Happens When Multiple Beans Match a Dependency?
Imagine our application has two implementations of PaymentRepository:
@Repository("sqlRepository")
public class SqlPaymentRepository
implements PaymentRepository {
}@Repository("memoryRepository")
public class InMemoryPaymentRepository
implements PaymentRepository {
}Both implement the same interface. So, when PaymentService requests a PaymentRepository, Spring finds two matching beans and cannot determine which one to inject by type alone.
We can solve this using @Qualifier or @Primary.
Option 1: Use @Qualifier to select a specific bean
@Service
public class PaymentService {
private final PaymentRepository repository;
public PaymentService(
@Qualifier("sqlRepository")
PaymentRepository repository) {
this.repository = repository;
}
}Here, @Qualifier("sqlRepository") tells Spring to inject the bean named sqlRepository.
Option 2: Use @Primary to define a default bean
Instead of specifying a bean at the injection point, we can mark our preferred implementation:
@Repository("sqlRepository")
@Primary
public class SqlPaymentRepository
implements PaymentRepository {
}Now, when PaymentService requests a PaymentRepository without a qualifier, Spring prefers SqlPaymentRepository.
The key difference:
@Qualifierselects a specific bean at the injection point.@Primarymarks the preferred bean when multiple candidates match.
This helped me realize that Dependency Injection isnβt just about passing an object into a constructor. Spring must also determine which bean should satisfy that dependency.
β 6. Why Constructor Injection?
One thing I wanted to understand was why constructor injection is commonly recommended.
@Service
public class PaymentService {
private final PaymentRepository repository;
public PaymentService(PaymentRepository repository) {
this.repository = repository;
}
}Some benefits are:
- Dependencies are clearly visible.
- Required dependencies can be stored in
finalfields. - Classes are easier to unit test.
- The class doesnβt need to create its own dependencies.
And an important Spring detail: if a class has only one constructor, Spring can use it for dependency injection without requiring @Autowired.
So instead of thinking, βConstructor injection is recommended because everyone says so,β I now understand why it is useful.
ποΈ 7. What Happens When a Spring Boot Application Starts?
Letβs connect everything.
@SpringBootApplication
public class PaymentApplication {
public static void main(String[] args) {
SpringApplication.run(
PaymentApplication.class, args
);
}
}At a high level, the startup process looks like this:
Application Starts
|
v
ApplicationContext Is Prepared
|
v
Configuration and Components Are Processed
|
v
Bean Definitions Are Registered
|
v
Beans Are Created and Dependencies Resolved
|
v
Application Becomes ReadyFor a typical web application, the embedded web server is also started during the startup process.
This is a simplified view; Spring performs additional configuration and lifecycle steps behind the scenes.
The key takeaway is that SpringApplication.run() does much more than execute our main method. It bootstraps the application context and coordinates the setup required to run the application.
π Interview Questions Iβm Adding to My Notes
Here are the questions I want to be able to explain in my own words:
- What happens when Spring discovers a class annotated with
@Component? - What is the difference between a bean definition and a Spring Bean?
- How does component scanning work in Spring Boot?
- What is the difference between
@Componentand@Bean? - Why would we use
@Beanfor a third-party class? - How does Spring resolve constructor dependencies?
- What happens when multiple beans match the same dependency?
- What is the difference between
@Primaryand@Qualifier? - Why is constructor injection preferred?
- What happens when
SpringApplication.run()executes?
π§ What I Learned From This Deep Dive
The biggest takeaway wasnβt another annotation.
It was understanding the process behind them.
Spring discovers eligible components, registers bean definitions, creates and manages objects, resolves dependencies, and connects everything through the application context.
Now, when I see @Service or @Repository, I want to understand more than just what the annotation means. I want to understand how that class becomes part of the running application.
Thatβs the kind of understanding Iβm trying to build as I prepare for my next move as a Java developer.
One concept down, many more to go. π
Thank you for reading and supporting my learning journey!
If this article helped you understand Spring a little better, please leave a clap π and share your thoughts in the comments. Iβd love to know which Spring concept you found confusing when you first started learning it.
See you in the next blog. Until then, keep learning, keep building, and keep growing. π»β
Coming next: Spring Bean Lifecycle Explained with Examples β understanding how Spring initializes beans and manages their lifecycle.
Learning in public, one concept at a time.
#Java #SpringBoot #Spring #JavaDeveloper #InterviewPreparation #SoftwareEngineering #LearningInPublic #BackendDevelopment









