Spring Circular Dependencies, Lazy Beans, and Component Scanning
Circular Dependency
Section titled “Circular Dependency”A circular dependency occurs when two beans depend on each other.
A -> BB -> Aflowchart LR A --> B B --> A
@Componentclass A { @Autowired B b;}
@Componentclass B { @Autowired A a;}This causes:
BeanCurrentlyInCreationExceptionResolution Strategies
Section titled “Resolution Strategies”Use @Lazy
Section titled “Use @Lazy”@Autowired@LazyB b;Use Setter Injection
Section titled “Use Setter Injection”@Componentclass A {
private B b;
@Autowired public void setB(B b){ this.b = b; }}Redesign the Architecture
Section titled “Redesign the Architecture”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 {}
@Componentclass 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:
@AutowiredObjectProvider<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:
@Lookuppublic PrototypeBean getPrototypeBean() { return null;}@Lazy delays bean creation until the bean is first requested.
@Component@Lazyclass PaymentService {}or
@Autowired@LazyPaymentService paymentService;Benefits:
- Faster startup
- Helps resolve circular dependencies
@PreDestroy
Section titled “@PreDestroy”Runs before bean destruction.
@PreDestroypublic void cleanup(){ System.out.println("Bean destroyed");}Typical uses:
- Closing connections
- Releasing resources
Component Scanning
Section titled “Component Scanning”Spring Boot automatically discovers beans using component scanning.
Internal Flow
Section titled “Internal Flow”@SpringBootApplicationincludes@ComponentScan.- Spring scans the base package and sub-packages.
- It detects stereotype annotations:
@Component@Service@Repository@Controller@RestController
- Creates
BeanDefinitionobjects. - 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:
@SpringBootApplicationpublic class Application { }Custom scanning:
@ComponentScan("com.example.service")Best Practice: Constructor Injection
Section titled “Best Practice: Constructor Injection”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.
Key Takeaways
Section titled “Key Takeaways”- 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
ObjectProviderto get a fresh prototype instance on each call. - Component scanning walks from
@SpringBootApplicationdown through the base package, detecting stereotype annotations and registeringBeanDefinitions in theApplicationContext. - Prefer constructor injection over field injection — it makes dependencies explicit, testable, and immutable.