Skip to main content
IoC and dependency injection · Topic 6 of 7

Circular dependencies

Definition​

A circular dependency means A needs B and B needs A. With constructor injection Spring cannot build either one first, so it fails at startup. The real fix is to redesign (move the shared logic into a third class); @Lazy is a last resort.

Example code​

The failure, and the redesign

import org.springframework.beans.BeansException;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

class OrderService {
OrderService(InvoiceService invoices) { } // needs InvoiceService...
}

class InvoiceService {
InvoiceService(OrderService orders) { } // ...which needs OrderService: a circle
}

class PricingRules { } // the fix: both depend on a third class instead

class OrderService2 { OrderService2(PricingRules rules) { } }
class InvoiceService2 { InvoiceService2(PricingRules rules) { } }

@Configuration
class Circular {
@Bean OrderService orders(InvoiceService invoices) { return new OrderService(invoices); }
@Bean InvoiceService invoices(OrderService orders) { return new InvoiceService(orders); }
}

@Configuration
class Fixed {
@Bean PricingRules rules() { return new PricingRules(); }
@Bean OrderService2 orders(PricingRules rules) { return new OrderService2(rules); }
@Bean InvoiceService2 invoices(PricingRules rules) { return new InvoiceService2(rules); }
}

class Demo {
public static void main(String[] args) {
try (var context = new AnnotationConfigApplicationContext(Circular.class)) {
System.out.println("started");
} catch (BeansException e) {
System.out.println("circular: " + e.getClass().getSimpleName());
}
try (var context = new AnnotationConfigApplicationContext(Fixed.class)) {
System.out.println("fixed: started");
}
}
}

// Output:
// circular: UnsatisfiedDependencyException
// fixed: started

Finished this page? Track your progress (saved only in your browser).