Sitemap
juhie sayyed

juhie sayyed

πŸš€ Java Developer Switch Preparation #03: What Spring Is Actually Doing Behind the Scenes

6 min readOct 8, 2026

--

Press enter or click to view image in full size

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 Injected

One 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 Spring

This 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:

  • @Qualifier selects a specific bean at the injection point.
  • @Primary marks 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 final fields.
  • 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 Ready

For 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 @Component and @Bean?
  • Why would we use @Bean for a third-party class?
  • How does Spring resolve constructor dependencies?
  • What happens when multiple beans match the same dependency?
  • What is the difference between @Primary and @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

--

--