Skip to content

Spring Circular Dependencies, Lazy Beans, and Component Scanning

A circular dependency occurs when two beans depend on each other.

A -> B
B -> A
flowchart LR
A --> B
B --> A
@Component
class A {
@Autowired
B b;
}
@Component
class B {
@Autowired
A a;
}

This causes:

BeanCurrentlyInCreationException
@Autowired
@Lazy
B b;
@Component
class A {
private B b;
@Autowired
public void setB(B b){
this.b = b;
}
}

Avoid circular dependencies whenever possible.

Injecting a Prototype Bean into a Singleton

Section titled “Injecting a Prototype Bean into a Singleton”

Yes, but only one prototype instance is injected during singleton creation.

@Component
@Scope("prototype")
class PrototypeBean {}
@Component
class SingletonBean {
@Autowired
PrototypeBean prototypeBean;
}
sequenceDiagram
    participant C as Spring Container
    participant S as Singleton
    participant P as Prototype

    C->>P: Create Prototype
    C->>S: Inject Prototype
    Note over S: Same prototype instance reused

To obtain a fresh prototype instance each time:

@Autowired
ObjectProvider<PrototypeBean> provider;
PrototypeBean bean = provider.getObject();

or

ApplicationContext.getBean(...)

or using @Lookup, which Spring overrides at runtime to return a fresh instance on each call:

@Lookup
public PrototypeBean getPrototypeBean() {
return null;
}

@Lazy delays bean creation until the bean is first requested.

@Component
@Lazy
class PaymentService {}

or

@Autowired
@Lazy
PaymentService paymentService;

Benefits:

  • Faster startup
  • Helps resolve circular dependencies

Runs before bean destruction.

@PreDestroy
public void cleanup(){
System.out.println("Bean destroyed");
}

Typical uses:

  • Closing connections
  • Releasing resources

Spring Boot automatically discovers beans using component scanning.

  1. @SpringBootApplication includes @ComponentScan.
  2. Spring scans the base package and sub-packages.
  3. It detects stereotype annotations:
    • @Component
    • @Service
    • @Repository
    • @Controller
    • @RestController
  4. Creates BeanDefinition objects.
  5. Registers them in the ApplicationContext.
flowchart TD
A[@SpringBootApplication] --> B[@ComponentScan]
B --> C[Scan base package]
C --> D[Find stereotype annotations]
D --> E[Create BeanDefinitions]
E --> F[Register in ApplicationContext]

Example:

@SpringBootApplication
public class Application { }

Custom scanning:

@ComponentScan("com.example.service")

Field injection is generally discouraged because it:

  • Makes testing harder
  • Breaks immutability
  • Hides dependencies

Constructor injection is the recommended approach — see the full constructor vs setter injection comparison for details.

  • Circular dependencies (BeanCurrentlyInCreationException) are best resolved with @Lazy, setter injection, or by redesigning the dependency graph — not as a permanent pattern.
  • Injecting a prototype bean into a singleton only wires one instance at creation time; use ObjectProvider to get a fresh prototype instance on each call.
  • Component scanning walks from @SpringBootApplication down through the base package, detecting stereotype annotations and registering BeanDefinitions in the ApplicationContext.
  • Prefer constructor injection over field injection — it makes dependencies explicit, testable, and immutable.