Component scanning is how Spring automatically finds your classes and registers them as beans.
Instead of manually creating every bean, you annotate classes with Spring stereotypes like:
@Component
@Service
@Repository
@Controller
@RestController
Then Spring scans selected packages, finds those classes, creates bean instances, and makes them available for dependency injection.
1. Basic Example
import org.springframework.stereotype.Service;
@Service
public class UserService {
public String getUserName() {
return "Alice";
}
}
Because UserService is annotated with @Service, Spring can discover it during component scanning.
You can inject it into another bean:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@GetMapping("/user")
public String getUser() {
return userService.getUserName();
}
}
Spring automatically:
- finds
UserService - creates a
UserServicebean - finds
UserController - creates a
UserControllerbean - injects
UserServiceintoUserController
2. Component Scanning in Spring Boot
In Spring Boot, component scanning is usually enabled automatically by @SpringBootApplication.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
@SpringBootApplication includes @ComponentScan.
By default, Spring Boot scans:
- the package containing the main application class
- all subpackages under it
For example:
com.example.demo
├── MyApplication.java
├── controller
│ └── UserController.java
├── service
│ └── UserService.java
└── repository
└── UserRepository.java
If MyApplication is in com.example.demo, Spring scans:
com.example.demo
com.example.demo.controller
com.example.demo.service
com.example.demo.repository
This is the recommended structure.
3. Important Package Rule
Your main application class should usually be in the root package.
Good:
com.example.app
├── Application.java
├── user
│ ├── UserController.java
│ └── UserService.java
└── order
├── OrderController.java
└── OrderService.java
Less ideal:
com.example.app.web
└── Application.java
com.example.app.service
└── UserService.java
If Application is inside com.example.app.web, Spring Boot scans com.example.app.web and its subpackages, but not sibling packages like com.example.app.service.
4. Manually Configure Component Scanning
If needed, you can specify packages explicitly with @ComponentScan.
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = "com.example.app")
public class AppConfig {
}
Or with multiple packages:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackages = {
"com.example.app.service",
"com.example.app.repository",
"com.example.shared"
})
public class AppConfig {
}
In Spring Boot, you can also place it on your main class:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan(basePackages = {
"com.example.app",
"com.example.shared"
})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
5. Prefer basePackageClasses for Type Safety
Instead of using package names as strings, you can use classes as package markers.
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan(basePackageClasses = {
UserService.class,
SharedComponent.class
})
public class AppConfig {
}
Spring scans the packages where those classes are located.
This is safer than string package names because refactoring tools can update class references.
6. Common Component Annotations
@Component
Generic Spring-managed bean.
import org.springframework.stereotype.Component;
@Component
public class FileStorage {
}
Use this when no more specific annotation fits.
@Service
Business logic or service layer.
import org.springframework.stereotype.Service;
@Service
public class PaymentService {
}
@Repository
Persistence or data access layer.
import org.springframework.stereotype.Repository;
@Repository
public class JdbcUserRepository {
}
For Spring Data JPA, repository interfaces are often detected separately by repository scanning:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
You usually do not need to add @Repository to Spring Data JPA interfaces.
@Controller
Spring MVC controller that usually returns views.
import org.springframework.stereotype.Controller;
@Controller
public class PageController {
}
@RestController
REST API controller.
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HealthController {
@GetMapping("/health")
public String health() {
return "OK";
}
}
@RestController is equivalent to @Controller plus @ResponseBody.
7. Injecting Scanned Components
Once a class is discovered by component scanning, inject it using constructor injection.
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final PaymentService paymentService;
public OrderService(PaymentService paymentService) {
this.paymentService = paymentService;
}
public void placeOrder() {
paymentService.charge();
}
}
With Lombok:
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class OrderService {
private final PaymentService paymentService;
public void placeOrder() {
paymentService.charge();
}
}
8. Excluding Classes from Component Scanning
You can exclude specific classes or patterns.
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
basePackages = "com.example.app",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = ExperimentalService.class
)
)
public class AppConfig {
}
You can also exclude by annotation:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
basePackages = "com.example.app",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = DeprecatedComponent.class
)
)
public class AppConfig {
}
9. Common Problems
Problem: Bean Not Found
Example error:
No qualifying bean of type 'com.example.UserService' available
Common causes:
- the class is not annotated with
@Component,@Service, etc. - the class is outside the scanned package
- the class is abstract
- the class has a failing constructor dependency
- a required profile is not active
- the bean is excluded by a scan filter
Problem: Controller Endpoint Not Working
Check that:
- the controller has
@Controlleror@RestController - it is inside a scanned package
- request mappings are correct
- Spring MVC is enabled/configured
- the application started without bean creation errors
Problem: Multiple Beans Found
If multiple scanned classes implement the same interface:
public interface PaymentProcessor {
void process();
}
import org.springframework.stereotype.Service;
@Service
public class StripePaymentProcessor implements PaymentProcessor {
@Override
public void process() {
// process with Stripe
}
}
import org.springframework.stereotype.Service;
@Service
public class PaypalPaymentProcessor implements PaymentProcessor {
@Override
public void process() {
// process with PayPal
}
}
Injection by interface becomes ambiguous:
import org.springframework.stereotype.Service;
@Service
public class CheckoutService {
public CheckoutService(PaymentProcessor paymentProcessor) {
}
}
Fix it with @Primary:
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
@Service
@Primary
public class StripePaymentProcessor implements PaymentProcessor {
@Override
public void process() {
// process with Stripe
}
}
Or with @Qualifier:
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class CheckoutService {
private final PaymentProcessor paymentProcessor;
public CheckoutService(
@Qualifier("paypalPaymentProcessor") PaymentProcessor paymentProcessor
) {
this.paymentProcessor = paymentProcessor;
}
}
10. Quick Rule of Thumb
For most Spring Boot applications:
- Put your main application class in the root package.
- Put controllers, services, repositories, and components in subpackages.
- Annotate classes with the right stereotype annotation.
- Use constructor injection.
- Avoid custom
@ComponentScanunless you really need it.
A typical structure:
com.example.app
├── Application.java
├── controller
│ └── UserController.java
├── service
│ └── UserService.java
├── repository
│ └── UserRepository.java
└── config
└── AppConfig.java
With this structure, Spring Boot component scanning usually works with no extra configuration.
