Dependency Injection & Beans - @Component, @Service, @Repository
This tutorial dives into the core concepts of Dependency Injection (DI) and
Beans in Spring Boot, focusing on the annotations @Component,
@Service, and @Repository. Understanding these is
crucial for building maintainable and testable Spring applications.
What are Beans?
In Spring, a Bean is a managed object. Think of it as a component that Spring creates, configures, and manages the lifecycle of. Beans are the building blocks of a Spring application. They can be anything from simple data objects to complex business logic components.
What is Dependency Injection?
Dependency Injection is a design pattern where objects receive their dependencies from external sources rather than creating them themselves. This promotes loose coupling, making your code more flexible, testable, and reusable.
Why use Dependency Injection?
- Loose Coupling: Components are less reliant on specific implementations of their dependencies.
- Testability: Easily mock dependencies for unit testing.
- Reusability: Components can be reused in different contexts with different dependencies.
- Maintainability: Changes to one component are less likely to impact others.
Spring's Role in Dependency Injection
Spring's IoC (Inversion of Control) container is responsible for managing beans and injecting dependencies. You tell Spring what you need, and Spring figures out how to provide it.
Bean Creation with Annotations
Spring provides several annotations to mark classes as beans. Let's explore the most common ones:
@Component: The most generic annotation. It marks a class as a Spring-managed component. Use this for classes that don't fit neatly into the@Serviceor@Repositorycategories.@Service: A specialization of@Componentthat indicates a class containing business logic. It's often used for service layers in your application. Provides semantic information.@Repository: A specialization of@Componentthat indicates a class responsible for data access. Typically used for interacting with databases or other data sources. Provides semantic information and enables translation of exceptions specific to data access technologies.
Example:
// A simple component
@Component
public class MyComponent {
public String sayHello() {
return "Hello from MyComponent!";
}
}
// A service component
@Service
public class MyService {
private final MyComponent myComponent;
// Constructor Injection - Preferred approach
public MyService(MyComponent myComponent) {
this.myComponent = myComponent;
}
public String getGreeting() {
return "Greeting from MyService: " + myComponent.sayHello();
}
}
// A repository component
@Repository
public class MyRepository {
// Simulate data access
public String getData() {
return "Data from MyRepository";
}
}
Explanation:
- We've annotated three classes:
MyComponent,MyService, andMyRepository. - Spring will automatically detect these classes (more on that later) and create instances of them as beans.
MyServicedepends onMyComponent. We use constructor injection to tell Spring to provide an instance ofMyComponentwhen creatingMyService. This is the recommended approach. Spring will automatically resolve this dependency.
How Spring Detects Beans
Spring uses component scanning to find classes annotated with @Component, @Service, @Repository, and other similar annotations.
@SpringBootApplication: This annotation (typically in your main application class) implicitly enables component scanning for the current package and its subpackages.@ComponentScan: You can use this annotation to explicitly specify the packages to scan for components. This is useful if your components are not in the same package as your main application class.
Example:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
In this example, Spring will scan the com.example package (assuming MyApplication is in that package) and all its subpackages for components.
Injecting Beans
There are several ways to inject dependencies in Spring:
- Constructor Injection (Recommended): Pass dependencies as constructor arguments. This makes dependencies explicit and ensures immutability. (See example above)
- Setter Injection: Use setter methods to inject dependencies.
- Field Injection: Use the
@Autowiredannotation directly on fields. (Generally discouraged due to testing difficulties and reduced clarity).
Example (Setter Injection):
@Service
public class MyService {
private MyComponent myComponent;
@Autowired
public void setMyComponent(MyComponent myComponent) {
this.myComponent = myComponent;
}
public String getGreeting() {
return "Greeting from MyService: " + myComponent.sayHello();
}
}
@Autowired: This annotation tells Spring to automatically resolve and inject the dependency.
Using the Beans
Now that we've defined and injected the beans, let's see how to use them. You can access beans in various ways:
- In other beans: As demonstrated in the
MyServiceexample. - In controllers: To provide data to your web application.
- In tests: To isolate and test components.
Example (Controller):
@RestController
public class MyController {
private final MyService myService;
public MyController(MyService myService) {
this.myService = myService;
}
@GetMapping("/greeting")
public String getGreeting() {
return myService.getGreeting();
}
}
Summary
- Beans are managed objects in Spring.
- Dependency Injection promotes loose coupling and testability.
@Componentis the generic annotation for marking a class as a bean.@Serviceindicates a class containing business logic.@Repositoryindicates a class responsible for data access.- Spring uses component scanning to find beans.
- Constructor injection is the preferred method for injecting dependencies.