IoC, Dependency Injection, Beans & Lifecycle

Overview

This page covers how Spring creates and wires your objects — the heart of the framework. After this you should be able to explain what a bean is, how it gets created, how dependencies are injected, and the traps interviewers love.

Official: IoC Container.


Table of contents

  1. Bean = object managed by Spring
  2. How Spring finds beans
  3. Stereotypes
  4. @Component vs @Bean
  5. Injection: constructor, setter, field
  6. Bean scopes
  7. @Primary and @Qualifier
  8. Lifecycle: @PostConstruct / @PreDestroy
  9. Circular dependencies
  10. Interview questions
  11. Summary

1. Bean = object managed by Spring

In plain Java you new everything. In Spring you tell the container “create one OrderRepository and one OrderService, and give the repository to the service”. The resulting objects are beans.

Default scope is singleton — one shared instance per container. That’s safe as long as the bean is stateless (no per-user mutable fields).


2. How Spring finds beans

When you put @SpringBootApplication on com.example.demo.DemoApplication, Spring scans:

  • com.example.demo and all sub-packages

for classes annotated with @Component (or any stereotype that includes @Component).

Common mistake: a service in com.example.orders while main is in com.example.demo → Spring never sees it. Move the class under the scan root, or add @ComponentScan("com.example.orders").


3. Stereotypes

All of these mean “this class is a bean” — they’re variants of @Component.

Annotation Used for Extra
@Component Generic bean
@Service Business logic Just a label
@Repository DB access Adds exception translation (SQL → DataAccessException)
@Controller Web MVC returning views Used with templates
@RestController REST APIs @Controller + @ResponseBody (returns JSON)

4. @Component vs @Bean

@Component @Bean
Where On a class On a method inside @Configuration
Who creates the instance Spring (calls constructor) You (return new Thing())
Use for Your own classes Third-party classes you can’t annotate
@Configuration
public class AppConfig {
    @Bean
    Clock clock() { return Clock.systemUTC(); }
}

5. Injection: constructor, setter, field

Constructor injection — prefer this

@Service
public class OrderService {
    private final OrderRepository orders;

    public OrderService(OrderRepository orders) {
        this.orders = orders;
    }
}
  • Dependencies are visible in the signature.
  • Fields can be final → immutable, thread-safe references.
  • Tests can call new OrderService(mockRepo) without Spring.
  • A class with a single constructor is autowired automatically — no @Autowired needed.

Setter injection

For optional dependencies. Rare in modern code.

Field injection (@Autowired on a field)

Hides dependencies, prevents final, hard to test. Avoid.


6. Bean scopes

Scope Meaning Use for
singleton (default) One instance per context Most services, repositories
prototype New instance every time it’s asked for Stateful builders
request / session One per HTTP request / session Rare in modern stateless APIs

Classic trap — prototype in singleton:

You make ReportBuilder prototype, inject it into singleton ReportService. Spring builds the service once, so it gets one builder forever — not a fresh one per call.

Fix: inject ObjectProvider<ReportBuilder> or use a scoped proxy.


7. @Primary and @Qualifier

When two beans implement the same interface:

  • @Primary on one bean — “prefer this one by default”.
  • @Qualifier("emailSender") on the injection point — pick by name.
public AlertService(@Qualifier("smsSender") NotificationSender s) { ... }

Otherwise Spring fails at startup with NoUniqueBeanDefinitionException.


8. Lifecycle: @PostConstruct / @PreDestroy

@PostConstruct
void init() { /* runs once after injection */ }

@PreDestroy
void cleanup() { /* runs on shutdown */ }

For “app is fully ready” (not just one bean), listen for ApplicationReadyEvent instead.


9. Circular dependencies

A needs B, B needs A. With constructor injection, Spring can’t construct either → startup fails with BeanCurrentlyInCreationException.

Fixes:

  1. Refactor — extract a third class so the graph is a DAG. Best fix.
  2. @Lazy on one constructor parameter — proxies the dependency, resolved on first use.
  3. ObjectProvider<X> — deferred lookup.

10. Interview questions

Q: What is Inversion of Control? A: The framework, not your code, creates and wires objects. DI is how Spring delivers IoC.

Q: What is a bean? A: An object Spring instantiates, configures, and manages. It has a name, a type, and a scope (default singleton).

Q: Why constructor injection? A: Required dependencies are explicit, fields can be final, and tests can new the class without Spring.

Q: Default scope? A: Singleton — one instance per ApplicationContext.

Q: Does singleton mean thread-safe? A: No. It means one instance. Thread safety is your job if the bean has mutable state. Stateless services are the norm.

Q: Two beans of the same type — what happens? A: Spring throws NoUniqueBeanDefinitionException. Fix with @Primary or @Qualifier.

Q: Prototype injected into singleton — what’s the trap? A: The singleton captures one prototype instance forever. Use ObjectProvider<T> to fetch a fresh one per call.

Q: Circular dependency — how do you fix it? A: Refactor first. Tactically, @Lazy on one constructor parameter or ObjectProvider works.

Q: Can you new a @Service yourself? A: Yes, but it won’t be a managed bean — no proxy, so @Transactional, @Async, etc. won’t work.


11. Summary

Idea One line
Bean Object Spring creates and manages
DI Dependencies passed in via constructor
Scan Package of @SpringBootApplication + sub-packages
Scope Singleton by default; watch the prototype-in-singleton trap
Lifecycle @PostConstruct / @PreDestroy

Next steps


Happy Learning

Next: Auto-Configuration & Starters