🧭
@ComponentScan이란?
- Spring이 지정된 패키지를 스캔해서
@Component,@Service,@Repository,@Controller가 붙은 클래스를 자동으로 Bean으로 등록하는 기능
👨🍳 비유: 셰프가 자동으로 재료를 찾아 주는 저장고
@ComponentScan은 셰프(Spring)가 요리에 필요한 재료(Bean)를 식료품 저장고(패키지)에서 자동으로 찾아 주는 기능을 의미함🔍 @ComponentScan의 동작 순서
@ComponentScan이 지정된 패키지부터 탐색 시작@Component 또는 유사 어노테이션(@Service 등)이 붙은 클래스를 탐색🔧 주요 속성
| 속성 | 설명 | 예시 |
|---|---|---|
basePackages | 스캔할 패키지 지정 | @ComponentScan(basePackages = {"com.app"}) |
basePackageClasses | 특정 클래스가 속한 패키지 기준 | @ComponentScan(basePackageClasses = App.class) |
excludeFilters | 특정 클래스 제외 | @ComponentScan(excludeFilters = ...) |
includeFilters | 특정 클래스만 포함 | @ComponentScan(includeFilters = ...) |
🗒️ 예시 코드
@Component
public class MyService {
public void serve() {
System.out.println("서비스 실행");
}
}
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyService service = context.getBean(MyService.class);
service.serve();
}
}
🛠️
@Configuration,@Bean
- 개발자가 직접 Bean을 명시적으로 등록하는 방식
- 주로 외부 라이브러리 객체 등록, 조건에 따라 다른 구현체 사용 시 유용
1. 자동 Bean 등록(@ComponentScan, @Component)
@Component가 있는 클래스의 앞글자만 소문자로 변경하여 Bean 이름으로 등록// myService 라는 이름의 Spring Bean
@Component
public class MyService {
public void doSomething() {
System.out.println("Spring Bean 으로 동작");
}
}
→ @ComponentScan 을 통해 @Component로 설정된 클래스 탐색
2. 수동 Bean 등록(@Configuration, @Bean)
@Configuration이 있는 클래스를 Bean으로 등록하고,@Bean 이 있는 메서드를 찾아 Bean 생성// 인터페이스
public interface TestService {
void doSomething();
}
// 인터페이스 구현체
public class TestServiceImpl implements TestService {
@Override
public void doSomething() {
System.out.println("Test Service 메서드 호출");
}
}
// 수동으로 빈 등록
@Configuration
public class AppConfig {
// TestService 타입의 Spring Bean 등록
@Bean
public TestService testService() {
// TestServiceImpl을 Bean으로 등록
return new TestServiceImpl();
}
}
// Spring Bean으로 등록이 되었는지 확인
public class MainApp {
public static void main(String[] args) {
// Spring ApplicationContext 생성 및 설정 클래스(AppConfig) 등록
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 등록된 TestService 빈 가져오기
TestService service = context.getBean(TestService.class);
// 빈 메서드 호출
service.doSomething();
}
}
❗ 주의: 반드시 @Configuration이 있어야 싱글톤 보장됨
@Bean만 있을 경우 싱글톤 보장이 안 될 수 있음⚔️ Bean 충돌
- 수동, 자동 두 가지가 존재하고, Bean은 각각의 이름으로 생성
- 이때 이름이 같은 Bean이 설정되고자 한다면 충돌 발생
1️⃣ 자동 vs 자동 충돌
@Component("service")
public class V1 implements MyInterface {}
@Component("service")
public class V2 implements MyInterface {}
→ ConflictingBeanDefinitionException 발생
→ 이름이 같은 Bean이 2개 이상 존재하면 충돌
2️⃣ 수동 vs 자동 충돌
@Component("conflictService") // 자동 등록
public class ConflictService implements MyService {}
@Configuration
public class ConflictConfig {
@Bean(name = "conflictService") // 수동 등록
public MyService conflictService() {
return new ConflictServiceV2();
}
}
→ 수동 등록된 Bean이 자동 등록을 오버라이딩(덮어씀)
→ 의도적일 수도 있지만, 대부분 실수 → 버그 발생 위험
⚙️ 해결 방법
✅ 설정에서 허용
spring.main.allow-bean-definition-overriding=true
🚫 기본값은 false (충돌 시 오류 발생)
🔄 자동 vs 수동 등록 비교
| 항목 | 자동 등록 | 수동 등록 |
|---|---|---|
| 방법 | @Component, @Service, @Repository | @Bean, @Configuration |
| 제어 수준 | 낮음 (자동 등록) | 높음 (직접 제어 가능) |
| 용도 | 대부분의 일반 서비스나 DAO 등 | 외부 라이브러리, 조건부 설정 등 |
| 충돌 시 | Spring Boot에서는 기본적으로 오류 | 수동이 자동을 오버라이드 |