Dependency Injection
At the end of this section, you should know
- How to instantiate bean?
- What is circular dependency and its resolution?
- Constructor-based DI vs setter-based DI and when to prefer which
Dependency Injection (DI) is a process where objects declare their dependencies via constructor arguments, factory method arguments, or properties. These dependencies are injected by the Spring container, reversing the control from the object managing its dependencies (Inversion of Control).
Using DI leads to cleaner, more testable code by decoupling objects. Dependencies are typically defined as interfaces or abstract classes, enabling the use of stubs or mocks in unit tests.
Spring supports two DI variants:
- Constructor-based DI
- Setter-based DI
Constructor-based Dependency Injection
The Spring container invokes a constructor with arguments representing dependencies. Example:
Java Class
public class SimpleMovieLister { private final MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}XML Configuration
<bean id="movieFinder" class="com.example.MovieFinder" /><bean id="movieLister" class="com.example.SimpleMovieLister"> <constructor-arg ref="movieFinder" /></bean>Annotation Configuration
@Componentpublic class SimpleMovieLister { private final MovieFinder movieFinder;
@Autowired public SimpleMovieLister(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}If a bean is a dependency of another bean, that usually means that one bean is set as a property of
another. Typically, you accomplish this with the <ref/> element in XML-based metadata or through
autowiring.
Resolving Constructor Arguments
- By Type: Spring matches arguments by their type.
- By Index: Explicitly specify argument positions.
- By Name: Use the
nameattribute in XML or parameter names annotated with@ConstructorProperties.
Example
Java Class
public class ExampleBean { private final int years; private final String answer;
public ExampleBean(int years, String answer) { this.years = years; this.answer = answer; }}XML Configuration
<bean id="exampleBean" class="com.example.ExampleBean"> <constructor-arg name="years" value="42" /> <constructor-arg name="answer" value=""The Answer" /></bean>Annotation Configuration
@Componentpublic class ExampleBean { private final int years; private final String answer;
@Autowired public ExampleBean(@Value("42") int years, @Value("The Answer") String answer) { this.years = years; this.answer = answer; }}Setter-based Dependency Injection
The Spring container calls setter methods to inject dependencies after invoking a no-argument constructor.
Java Class
public class SimpleMovieLister { private MovieFinder movieFinder;
public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}XML Configuration
<bean id="movieFinder" class="com.example.MovieFinder" /><bean id="movieLister" class="com.example.SimpleMovieLister"> <property name="movieFinder" ref="movieFinder" /></bean>Annotation Configuration
@Componentpublic class SimpleMovieLister { private MovieFinder movieFinder;
@Autowired public void setMovieFinder(MovieFinder movieFinder) { this.movieFinder = movieFinder; }}Constructor vs. Setter Injection
- Use constructor injection for mandatory dependencies. This approach ensures immutability and that all required dependencies are set at initialization.
- Use setter injection for optional dependencies or when reconfiguration is needed.
Circular Dependencies
Circular dependencies (e.g., A depends on B and B depends on A) are problematic with constructor
injection but can be resolved with setter injection. Spring detects circular references and throws
a BeanCurrentlyInCreationException when they are unresolvable.
One possible solution is to edit the source code of some classes to be configured by setters rather than constructors. Alternatively, avoid constructor injection and use setter injection only. In other words, although it is not recommended, you can configure circular dependencies with setter injection.
Unlike the typical case (with no circular dependencies), a circular dependency between bean A and bean B forces one of the beans to be injected into the other prior to being fully initialized itself (a classic chicken-and-egg scenario).
Dependency Injection Examples
XML Example
<bean id="exampleBean" class="com.example.ExampleBean"> <property name="beanOne" ref="anotherExampleBean" /> <property name="beanTwo" ref="yetAnotherBean" /> <property name="integerProperty" value="1" /></bean>
<bean id="anotherExampleBean" class="com.example.AnotherBean" /><bean id="yetAnotherBean" class="com.example.YetAnotherBean" />Annotation Example
@Componentpublic class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int integerProperty;
@Autowired public void setBeanOne(AnotherBean beanOne) { this.beanOne = beanOne; }
@Autowired public void setBeanTwo(YetAnotherBean beanTwo) { this.beanTwo = beanTwo; }
@Value("1") public void setIntegerProperty(int integerProperty) { this.integerProperty = integerProperty; }}Best Practices
- Favor constructor injection for required dependencies.
- Use setter injection for optional dependencies.
- Keep DI configurations minimal and focused.
- Avoid circular dependencies where possible.