Spring Bean Autowiring is a core feature that simplifies dependency injection in a Spring application. For automatic dependency injection, the Spring framework recommends using constructor injection for mandatory dependencies and the setter injection for optional dependencies.
In this Spring tutorial, we’ll understand the basics of Spring Bean Autowiring with various examples and best practices
1. Autowiring by Constructor: The Recommended Approach
Autowiring by constructor is a clean and concise way to inject dependencies. In this approach, we utilize the constructor arguments to inject the dependencies into the bean.
Let’s extend our example with a constructor. In the following code, the Shape
dependency is injected through the constructor when the application starts.
This approach is also called the constructor injection and is recommended for the “mandatory“ dependencies.
import org.springframework.beans.factory.annotation.Autowired;
public class ShapeService {
private final Shape shape;
public ShapeService(Shape shape) {
this.shape = shape;
}
// Other methods...
}
2. The @Autowired Annotation
The @Autowired
annotation in Spring Framework is used for automatic dependency injection. It can be applied to fields, setter methods, and constructors to indicate that Spring should automatically provide the required dependencies.
Consider a ShapeService
class that requires a Shape
. In this example, Spring will automatically inject a Shape
bean into the shape
field when creating an instance of ShapeService
.
This approach is also called the setter injection and is recommended for the “optional“ dependencies.
import org.springframework.beans.factory.annotation.Autowired;
public class ShapeService {
private Shape shape;
@Autowired
public void setShape(Shape shape) {
this.shape = shape;
}
}
3. Using @Qualifier for Conflict Resolution
Let’s recap the setter injection (autowire byType).
- If there is exactly one bean of the property type in the container, it injects that bean.
- If there are multiple candidates of the same type, Spring throws NoUniqueBeanDefinitionException because it cannot determine which one to inject.
- In cases where no matching bean is found:
- Spring throws NoSuchBeanDefinitionException if the dependency is required.
- The field is unset (remains null) if the dependency is optional.
The @Qualifier annotation helps in solving the NoUniqueBeanDefinitionException when there are two matching beans of the same type.
Consider an example where we have two beans of type Shape i.e. Circle and Square.
@Configuration
@ComponentScan
public class BeanConfig {
@Bean
Square square() {
return new Square();
}
@Bean
Circle circle() {
return new Circle();
}
}
We we run the program then we will get the runtime error when injecting the Shape into ShapeService. To fix the issue, we must ensure which exact bean we want to inject into the dependency and then use @Qualifier to refer to the bean by its name.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class ShapeService {
@Autowired
@Qualifier("circle")
private Shape shape;
// Other methods...
}
4. Optional Dependencies with ‘required=false‘ (Not Recommended)
Even if you have used the utmost care in autowiring bean dependencies, still you may find strange bean lookup failures. In this case, you want to indicate that a dependency is optional. If Spring cannot find a matching bean for the dependency, it can inject null
if the dependency is marked as optional. This can be achieved by setting the required
attribute of @Autowired
to false
.
import org.springframework.beans.factory.annotation.Autowired;
public class ShapeService {
@Autowired(required = false)
private Shape shape;
// Other methods...
}
5. Circular Dependencies and Autowiring
In a Spring application, if there is a circular dependency between two or more beans, Spring will create proxies or use lazy initialization to resolve the circular reference.
Consider this example where bean A and bean B, both refer to each other. When Spring attempts to create beans for these classes, it encounters a circular reference. Which one will be initialized first?
public class A {
private B b;
@Autowired
public A(B b) {
this.b = b;
}
// Other methods...
}
public class B {
private A a;
@Autowired
public B(A a) {
this.a = a;
}
// Other methods...
}
Internally Spring will use either proxy objects or lazy initialization technique or a combination of both.
- In the proxy object approach, a proxy object is created for one of the circular dependencies. The actual instance is then injected once it’s fully constructed.
- In the lazy initialization approach, Spring creates partially initialized instances of beans involved in the circular reference and then completes their initialization later. This allows Spring to break the circular reference temporarily during the bean creation phase.
6. Best Practices
Though Spring does a lot behind the scenes, we also must follow consistent approaches to create a maintainable code. For example:
- Adopt consistent naming conventions for beans and properties to leverage autowiring effectively.
- Prefer autowiring by constructor for cleaner code and better testability.
- While
@Qualifier
is useful, we should use it in very specific scenarios where other mechanisms are failing. - Use
required = false
for dependencies that are genuinely optional. Do not use it to suppress the errors. - Be aware of the bean lifecycle and potential issues with circular dependencies.
- Always use Java-based configuration for its conciseness and type safety.
7. Conclusion
Spring Bean Autowiring is achieved by the @Autowired
annotation and it significantly simplifies dependency injection in a Spring application. Understanding the basics of autowiring and when to use which technique is very important in writing bug-free and testable code.
That’s all about Spring bean autowiring. If you have any doubts, please drop a comment.
Happy Learning !!
Comments