Spring provides annotations like @Component
, @Autowired
, and @Qualifier
to simplify dependency injection and make applications loosely coupled and modular. Below, we’ll explore these annotations in detail, focusing on their usage and a complete example.
1. @Component
Annotation
The @Component
annotation marks a class as a Spring-managed bean. It is auto-detected during component scanning, and Spring adds it to the application context.
Usage:
import org.springframework.stereotype.Component;
@Component
public class ExampleComponent {
public void execute() {
System.out.println("Component is working!");
}
}
When the Spring application starts, it automatically scans the classpath for classes annotated with @Component
(and its specializations like @Service
, @Repository
, and @Controller
) and registers them as beans in the application context.
2. @Autowired
Annotation
The @Autowired
annotation is used for automatic dependency injection. It instructs Spring to inject a matching bean from the application context where the annotation is applied.
Types of Injection:
- Field Injection:
@Component public class ClientWithFieldInjection { @Autowired private ExampleComponent exampleComponent; public void perform() { exampleComponent.execute(); } }
- Setter Injection:
@Component public class ClientWithSetterInjection { private ExampleComponent exampleComponent; @Autowired public void setExampleComponent(ExampleComponent exampleComponent) { this.exampleComponent = exampleComponent; } public void perform() { exampleComponent.execute(); } }
- Constructor Injection (Preferred):
@Component public class ClientWithConstructorInjection { private final ExampleComponent exampleComponent; @Autowired public ClientWithConstructorInjection(ExampleComponent exampleComponent) { this.exampleComponent = exampleComponent; } public void perform() { exampleComponent.execute(); } }
- Preferred: Constructor injection is considered a best practice because:
- Dependencies are initialized during object creation, ensuring immutability.
- It’s easier to write unit tests, as all dependencies can be provided explicitly.
3. @Qualifier
Annotation
When multiple beans of the same type exist in the application context, Spring must decide which one to inject. By default, it uses the bean name, but you can explicitly specify which bean to use with the @Qualifier
annotation.
Complete Example with Interface, Implementations, and Dependency Injection
Step 1: Define an Interface
Create an abstraction to represent a service contract.
public interface ServiceA {
void serve();
}
Step 2: Provide Implementations
Implement the ServiceA
interface with two different classes.
import org.springframework.stereotype.Component;
@Component("serviceAImpl1")
public class ServiceAImpl1 implements ServiceA {
@Override
public void serve() {
System.out.println("ServiceAImpl1 is serving...");
}
}
@Component("serviceAImpl2")
public class ServiceAImpl2 implements ServiceA {
@Override
public void serve() {
System.out.println("ServiceAImpl2 is serving...");
}
}
- The
@Component("serviceAImpl1")
and@Component("serviceAImpl2")
annotations allow Spring to identify and differentiate the two beans. The specified names (serviceAImpl1
andserviceAImpl2
) can be used with the@Qualifier
annotation.
Step 3: Inject the Dependency in the Client Class
Create a client class that depends on ServiceA
.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class Client {
private final ServiceA serviceA;
@Autowired
public Client(@Qualifier("serviceAImpl1") ServiceA serviceA) { // Use serviceAImpl1
this.serviceA = serviceA;
}
public void run() {
serviceA.serve();
}
}
@Qualifier("serviceAImpl1")
: Ensures that the specific implementationServiceAImpl1
is injected into theClient
class. Without the qualifier, Spring would throw an error due to ambiguity, as multiple beans (serviceAImpl1
andserviceAImpl2
) implement the same interface.
Step 4: Application Entry Point
Run the application with @SpringBootApplication
to trigger Spring’s component scanning and dependency injection.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
var context = SpringApplication.run(Application.class, args);
// Get the client bean from the Spring application context
Client client = context.getBean(Client.class);
client.run(); // This will call ServiceAImpl1's serve() method.
}
}
How It Works
- Component Scanning:
- Spring automatically scans for all classes annotated with
@Component
and registers them as beans in the application context.
- Spring automatically scans for all classes annotated with
- Dependency Injection:
- Spring injects
ServiceAImpl1
intoClient
using the@Autowired
and@Qualifier
annotations.
- Spring injects
- Output: Upon running the application, the following message is printed:
ServiceAImpl1 is serving...
Key Advantages of Using Interfaces and Dependency Injection
- Loose Coupling: The client depends on an abstraction (
ServiceA
) rather than concrete classes, making the application flexible and easier to maintain. - Testability: By using interfaces, you can easily mock or stub dependencies for testing purposes.
- Flexibility: New implementations can be added and swapped out without changing the client code.
Additional Notes on @Qualifier
- If only one implementation exists, you don’t need
@Qualifier
; Spring can find the appropriate bean automatically. - If you don’t use
@Qualifier
, and there are multiple matching beans, Spring throws aNoUniqueBeanDefinitionException
.
Final Thoughts
Using @Component
, @Autowired
, and @Qualifier
together allows you to create a clean and modular structure in Spring applications. By programming to an interface, following the best practice of constructor injection, and leveraging qualifiers for resolving ambiguities, you can develop highly extensible and maintainable applications.
Maven Dependencies
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.2.6</version>
</dependency>
</dependencies>