How Spring Container Works Behind the Scenes

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

✅ Some premium posts are free to read — no account needed. Follow me on Medium to stay updated and support my writing.

🎓 Top 10 Udemy Courses (Huge Discount): Explore My Udemy Courses — Learn through real-time, project-based development.

▶️ Subscribe to My YouTube Channel (172K+ subscribers): Java Guides on YouTube

If you’ve built even a single application with Spring or Spring Boot, you’ve already been using the Spring container — even if you didn’t realize it.

You’ve annotated your classes with @Service, @Repository, or @Component, and magically, they were created, injected, and managed without any manual new calls.

But what exactly is this “Spring container”? And what happens under the hood when your application starts?

In this article, we’ll break down exactly how the Spring container works behind the scenes — from classpath scanning to bean creation, dependency injection, and lifecycle management.

First: What Is the Spring Container?

The Spring container is the core part of the Spring Framework that manages the lifecycle and configuration of application beans.

It is responsible for:

  • Creating objects (beans)
  • Injecting dependencies
  • Managing bean scopes (singleton, prototype, etc.)
  • Handling the complete lifecycle of each bean

The container uses Inversion of Control (IoC) and Dependency Injection (DI) principles.

🔍 Key Components Behind the Container


🧭 Spring Container Workflow: Step-by-Step

Let’s walk through how Spring creates and manages your beans — behind the scenes.

🔹 1. Application Starts

When you run a Spring Boot app, the entry point is usually:

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

SpringApplication.run() does a lot behind the scenes, including:

  • Creating an instance of ApplicationContext
  • Loading configurations
  • Starting the container

🔹 2. Component Scan Begins

Using @ComponentScan, Spring scans all the classes in your base package and sub-packages.

It looks for:

  • @Component
  • @Service
  • @Repository
  • @Controller
  • @Configuration

Each matching class is registered as a bean definition.


🔹 3. Bean Definitions Are Registered

Each discovered class is turned into a BeanDefinition — a metadata object that tells Spring:

  • What class to use
  • What dependencies it needs
  • Its scope (singleton, prototype, etc.)
  • Lifecycle callbacks (@PostConstruct, @PreDestroy, etc.)

At this stage, no beans are created yet. It’s just preparing blueprints.


🔹 4. BeanFactoryPostProcessors Run

Before bean creation begins, any BeanFactoryPostProcessor beans are invoked. These are used to modify bean definitions before actual beans are instantiated.

Example:

  • PropertySourcesPlaceholderConfigurer replaces ${} placeholders with actual values.

🔹 5. Beans Are Instantiated (Created)

Now the container starts creating beans.

If you’re using constructor injection, Spring looks at the constructor:

@Service
public class OrderService {
public OrderService(OrderRepository repo) {
...
}
}

It looks into the constructor, finds required dependencies (OrderRepository), and recursively resolves those first.


🔹 6. Dependencies Are Injected

Dependencies are injected using:

  • Constructor Injection
  • Setter Injection
  • Field Injection (via reflection)

Spring uses reflection to find dependencies and inject them into the appropriate place.

It checks for:

  • @Autowired
  • @Inject
  • @Value

🔹 7. BeanPostProcessors Are Invoked

Before and after initialization, Spring allows special beans called BeanPostProcessors to run.

These are hooks for:

  • Custom logic (e.g., wrapping beans with proxies)
  • Annotation handling (@Autowired, @Transactional)
  • Logging or debugging bean creation

Example: AutowiredAnnotationBeanPostProcessor processes the @Autowired annotation.


🔹 8. Lifecycle Callbacks Are Triggered

If your bean implements:

  • InitializingBean – calls afterPropertiesSet()
  • DisposableBean – calls destroy()

Or if you’ve defined:

  • @PostConstruct – called after dependency injection
  • @PreDestroy – called before the bean is destroyed

These are lifecycle methods managed by the container.


🔹 9. Beans Are Cached (If Singleton)

If the bean scope is singleton (which it is by default), Spring stores it in a cache so that the same instance is reused everywhere.

For prototype scope, a new bean is created every time getBean() is called.


🔹 10. ApplicationContext Is Ready

Once all beans are created and initialized, the container is fully loaded.

Now your application can start serving requests, processing data, or interacting with external systems.


🧪 Real Example: Let’s Debug a Simple App

Let’s say you have:

@Service
public class ProductService {
private final ProductRepository repo;

public ProductService(ProductRepository repo) {
this.repo = repo;
}
}
@Repository
public class ProductRepository {
public List<String> findAll() {
return List.of("Apple", "Banana");
}
}

What Spring Does Behind the Scenes:

  1. Scans ProductService and ProductRepository
  2. Registers them as bean definitions
  3. Detects that ProductService needs ProductRepository
  4. Creates ProductRepository
  5. Injects it into ProductService via constructor
  6. Caches the instances
  7. Makes both beans available via context.getBean(...)

All automatically. No new, no wiring, no XML.


🔍 Why Is This Design So Powerful?

Because Spring:

  • Decouples object creation from logic
  • Allows easy testing with mocks and stubs
  • Provides flexibility to change implementations via configuration
  • Automatically wires dependencies even in large apps

And with tools like Spring Boot, most of this wiring is auto-configured.


🧱 Quick Summary: Spring Container Lifecycle

Application Starts

Component Scanning

BeanDefinition Registration

BeanFactoryPostProcessors

Bean Instantiation

Dependency Injection

BeanPostProcessors

Lifecycle Annotations (@PostConstruct)

Ready to Use!

✅ Final Thoughts

The Spring container is a beautiful piece of engineering. It quietly handles hundreds of tiny tasks for you — like instantiating objects, injecting dependencies, managing scopes, and running lifecycle hooks.

Understanding what’s happening behind the scenes helps you:

  • Write better Spring code
  • Debug issues faster
  • Design applications that scale
You don’t need to configure everything manually. But you should know what Spring is doing on your behalf.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare