DI and Autowiring via Annotation/Java Based Configuration
Three main ways to inject dependencies via Annotation:
- Field / Property Injection
- Constructor Injection
- Setter Injection
What is Autowiring?
Autowiring is automatic injection of dependencies by Spring. Instead of manually linking beans in XML or Java config, Spring can detect and inject them automatically.
What is Field Injection?
Putting the @Autowired annotation directly on a class field,
so Spring injects the dependency without using setters or constructors.
2. @Autowired Annotation
- Tells Spring to automatically inject a bean into another bean,
- letting the IoC container resolve and provide the dependency at runtime.
- Injects only reference types (beans), not primitives or Strings.
- can be applied to: Fields, Setter methods and Constructors.
- No control of Programmer.
@Autowired Resolution Rules
- Primary rule :
byType - If multiple beans of that type exist : Try
byNameresolution. - checks field name / parameter name with bean ID.
-
If a match is found then bean is injected.
-
If still ambiguous : throws
NoUniqueBeanDefinitionException(can fix with@Qualifieror@Primary)
Example Code
package com.spring;
@Component
public class Address{
public void Address(){}
}
package com.spring.pkg;
@Component("myStudent")
public class Student{
@Autowired // Field Injection
public Address address;
public void Student(){}
public Address getAddress(){return address; }
}
@Configuration
@ComponentScan("com.spring")
class AppConfig{
}
class Test{
ApplicationContext container = new AnnotationConfigApplicationContext(AppConfig.class);
Student s = container.getBean("myStudent", Student.class);
}
How it works:
- Spring scans the package.
- Finds Address and Student as
@Componentbeans. - Injects Address into Student’s address field automatically.
Question
Given
Dependency Type : Address
Available beans: address, address1
Case 1: Student : Address address
- type = Address (two beans exist).
- Field name=
addressmatches bean idaddress - DI success : injects
address
Case 2: Student : Address address1
- type = Address (two beans exist).
- Field name =
address1matches bean idaddress1. - DI Success: injects
address1
Case 3: Student : Address xyz
- type =
Address(two beans exist). - Field name =
xyzbut no matching bean id. - DI Failed: throws
NoUniqueBeanDefinitionException
3. Ambiguity Problem
Plain Java : Ambiguity arises with overloaded constructors + null arguments.
In Spring, if define multiple constructors, Spring doesn’t know which constructor to use for injection, leading to an error.
@Component
class Student {
private String name;
private int age;
public Student(String name) {this.name = name; }
public Student(int age) { this.age = age; }
}
Spring cannot figure out whether to call Student(String) or Student(int) when creating the bean.
3.1 Ways to solve Ambiguity
1. Mark the intended constructor with @Autowired
@Component
class Student {
private String name;
@Autowired
public Student(String name) {this.name = name;} // Spring will use this
public Student(int age) { } //spring Not used for Dependency Injection
}
2. Use @Qualifier to tell Spring which bean to inject
@Qualifierresolves conflicts(ambiguity) when multiple beans of the same type exist.- Must match
bean idor bean name exactly. - used with
@Autowiredfor constructor, setter, or field/property injection. - which bean id is quialified for DI process.
- Acts like
refattribute in XML perspective.
Example Code
// Address Class
package com.spring;
@Component("address1")
public class Address{
public void Address(){}
}
//Student Class
package com.spring.pkg;
@Component("myStudent")
public class Student{
@Autowired
Qualifier("address2")
public Address address;
public void Student(){}
}
//AppConfig Class
@Configuration
@ComponentScan("com.spring")
class AppConfig{
@Bean
public Address address2(){
return Address();
}
@Bean
public Address address3(){
return Address();
}
}
3. Use @Primary Annotation
- Marks a bean as default when multiple beans of same type exist.
- Works with
@Autowiredto avoid ambiguity. - Can be used on a class (
@Component) or a method (@Bean).
Remember:
- Avoid overloaded constructors in Spring-managed beans.
- Best practice: define only one constructor for injection.
4. DI with Interface and Implements
Scenario
class Vehicle(){
public String type();
}
@Component
class Car implements Vehicle(){
@override
public String type(){return "type :Car"}
}
@Component
class Bike implements Vehicle(){
@override
public String type(){return "type is Bike"}
}
@Component
class Garage(){
@Qualifier("bike")
@Autowired
private Vehicle vehicle;
}
What happens?
- spring scan for beans.
- creates bean object(
car,bike,garage). -
Garage class need a Vehicle
@Autowired+@Qualifier("bike")tells Spring:Don’t just pick any Vehicle, inject the bean with name bike. So Spring injects the Bike object.
-
Result
- Inside Garage, the vehicle field refers to the Bike bean.
- Calling
vehicle.type()will return:type is Bike
5. Priority between @Qualifier and @Primary
- @Qualifier : Has higher priority (explicit match).
- @Primary : Used only if no @Qualifier is specified.
- If neither is used :
NoUniqueBeanDefinitionExceptionwhen multiple beans exist.
So @Qualifier overrides @Primary.
6. Relation among @Autowired ,@Qualifier and @Primary
@Autowired→ performs injection.- If multiple beans → Spring looks for
@Qualifier. - If no
@Qualifier→ Spring falls back to@Primary. - If neither →
NoUniqueBeanDefinitionException.
↑ Back to top
Github Code : DI via Annotation : Spring Boot