In Spring Framework, autowiring is the feature that injects dependencies into a bean automatically. One of the autowiring modes is “byType“, which instructs the Spring IoC container to inject a bean of the same type as the property into which it is being injected. This is the default autowiring mode when using Java configuration.
For dependency injection in Spring framework, the recommened approach is to use the constructor injection.
1. What is Autowiring By Type?
When autowiring by type, Spring looks for a bean of the same type as the property being autowired.
- 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.
To mark a dependency optional, set the ‘required=false‘ in the @Autowired annotation.
@Autowired(required = false)
private Shape shape;
2. Understanding Autowiring By Type with Example
Suppose we have a ShapeService class that performs some action on the Shape object whatever is injected to it.
public class ShapeService {
@Autowired
private Shape shape;
public void drawShape() {
if (shape != null) {
shape.draw();
} else {
System.out.println("No shape set.");
}
}
}
The Shape is an abstract type that can have multiple implementations.
public abstract class Shape {
abstract void draw();
}
Now imagine we created two such implementations of Shape as follows:
public class Square extends Shape {
@Override
void draw() {
System.out.println("Drawing the Square");
}
}
public class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing the Circle");
}
}
To create the beans from the above classes, we create the BeanConfig class. Here we can define all the beans that must be present in the application context.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean
Square square() {
return new Square();
}
@Bean
Circle circle() {
return new Circle();
}
@Bean
ShapeService shapeService() {
return new ShapeService();
}
}
To run the program, we use the AnnotationConfigApplicationContext to load the beans and access any of the bean methods.
public class AutowiredByTypeExample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
ShapeService shapeService = context.getBean(ShapeService.class);
shapeService.drawShape();
}
}
2.1. Multiple Beans of the Same Type causes NoUniqueBeanDefinitionException
When we run the above application with the following bean configuration then Spring finds multiple candidates (Square and Circle) to inject into ShapeService so it throws an exception.
@Configuration
public class BeanConfig {
@Bean
Square square() {
return new Square();
}
@Bean
Circle circle() {
return new Circle();
}
@Bean
ShapeService shapeService() {
return new ShapeService();
}
}
When we run the program, we get the exception:
2023-11-30T13:34:21.450+0530 WARN Exception encountered during context initialization - cancelling refresh
attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
'shapeService': Unsatisfied dependency expressed through field 'shape': No qualifying bean of type
'com.howtodoinjava.core.autowire.byType.Shape' available: expected single matching bean but found 2: square,circle
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
with name 'shapeService': Unsatisfied dependency expressed through field 'shape': No qualifying bean of type
'com.howtodoinjava.core.autowire.byType.Shape' available: expected single matching bean but found 2: square,circle
2.2. Only a Single Matching Bean works Perfectly
Suppose we remove the Square (or Circle) bean. Since there is only one bean of type Shape
in the context (either Circle
or Square
), Spring will successfully autowire it, and you will see the appropriate draw message.
@Configuration
public class BeanConfig {
@Bean
Square square() {
return new Square();
}
@Bean
ShapeService shapeService() {
return new ShapeService();
}
}
Run the program and verify the output:
Drawing the Square
2.3 No Matching Bean causes NoSuchBeanDefinitionException
Comment out both Circle
and Square
beans in the configuration. Now Spring will not find any matching bean in the context. Spring will throw an exception saying the “expected at least 1 bean which qualifies as autowire candidate“.
@Configuration
public class BeanConfig {
@Bean
ShapeService shapeService() {
return new ShapeService();
}
}
Run the program and verify the output:
2023-11-30T13:51:46.863+0530 WARN Exception encountered during context initialization - cancelling refresh
attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name
'shapeService': Unsatisfied dependency expressed through method 'setShape' parameter 0: No qualifying bean of
type 'com.howtodoinjava.core.autowire.byType.Shape' available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
with name 'shapeService': Unsatisfied dependency expressed through method 'setShape' parameter 0: No qualifying
bean of type 'com.howtodoinjava.core.autowire.byType.Shape' available: expected at least 1 bean which qualifies as
autowire candidate. Dependency annotations: {}
3. Conclusion
Autowiring by type in Spring is a convenient way to let the Spring IoC container automatically inject dependencies based on their types. It simplifies configuration and reduces boilerplate code.
However be cautious of conflicts when multiple beans of the same type exist.
Happy Learning !!
Comments