컴포넌트 스캔(Component Scan)
스프링에서는 설정 정보(AppConfig.class) 없이 자동으로 스프링 빈을 등록하는 컴포넌트 스캔 기능을 제공한다.
스프링 빈을 등록할 때는 의존관계를 자바 코드로 직접 등록하는 방법과 컴포넌트 스캔과 자동 의존관계 설정하는 @Autowired를 사용하는 방법이 있다.
@ComponentScan은 탐색 위치에 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
AutoDependecyConfig.Java
@ComponentScan
public class AutoDependencyConfig {
}
이때 기본적으로 빈 이름은 맨 앞글자를 소문자로 바꿔 사용된다.
@Component(”beanName”)과 같이 빈 이름을 직접 설정해 줄 수도 있다.
탐색범위(basePackages)
모든 자바 클래스를 컴포넌트 스캔하면 시간이 오래 걸리게 된다. 따라서 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.
@ComponentScan(basePackages = "hello.spring") // hello.spring 패키지부터 스캔
public class AutoDependencyConfig {
}
basePackages : 탐색할 패키지의 시작 위치를 설정하고, 해당 패키지부터 하위 패키지까지 모두 탐색한다.
basePackageClasses : 통해 클래스가 속한 패키지를 탐색 시작 위치로 지정할 수 있다.
basePackages 또는 basePackageClasses를 지정하지 않으면, @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
따라서, 설정 정보 클래스의 위치를 프로젝트 최상단에 두고 패키지 위치는 지정하지 않는 방법이 속도면에서는 느릴 수 있으나 가장 편한 방법이기도 하다.
또한, 스프링 부트를 사용하면 일반적으로 @SpringBootApplication을 프로젝트 시작 루트 위치에 둔다.
@SpringBootApplicaiton에는 @ComponentScan이 포함되어 있다.
컴포넌트 스캔의 기본 대상
@ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
빈 이름 기본 전략 : MemberServiceImpl → memberServiceImpl
빈 이름 직접 지정 : @Component(”memberService2”)와 같이 빈의 이름을 직접 설정할 수 있다.
컴포넌트 스캔의 기본 대상
- @Component : 컴포넌트 스캔에서 사용된다.
- @Controller & @RestController : 스프링 MVC 및 REST 전용 컨트롤러에서 사용된다.
- @Service : 스프링 비즈니스 로직에 사용된다. 특별한 처리는 하지 않고, 비즈니스 계층을 인식하는데 도움이 된다.
- @Repository : 스프링 데이터 접근 계층에서 사용된다. 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다.
- @Configuration : 스프링 설정 정보에서 사용된다. 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.
추가적으로 @Controller, @Service, @Repository에는 기본적으로 @Component를 포함하고 있다.
필터(Filter)
필터는 컴포넌트 스캔의 대상 범위를 지정하는 데 사용한다.
includeFilters : 컴포넌트 스캔 대상을 추가로 지정한다.
excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정한다.
FilterType 옵션
- ANNOTATION : 기본값으로 어노테이션으로 인식해서 동작한다.
- ASSIGNABLE_TYPE : 지정한 타입과 자식 타입을 인식해서 동작한다.
- ASPECTJ : AspectJ 패턴을 사용한다.
- REGEX : 정규표현식을 나타낸다.
- CUSTOM : TypeFilter라는 인터페이스를 구현해서 처리한다.
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = IncludeClass.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = ExcludeClass.class)
)
public class AutoDependencyConfig {
}
컴포넌트 스캔의 중복 등록과 충돌
컴포넌트 스캔에서 같은 빈 이름을 등록하는 경우의 수는 두 가지가 있다.
- 자동 빈 등록과 자동 빈 등록의 충돌
- 수동 빈 등록과 자동 빈 등록의 충돌
자동 빈 등록과 자동 빈 등록의 충돌
컴포넌트 스캔에 의해 자동으로 스프링 빈이 등록될 때, 이미 이름이 같은 스프링 빈이 있는 경우 스프링은 ConflictingBeanDefinitionException 예외를 발생시킨다.
자동 빈 등록
@Component("service") // service 이름으로 컴포넌트 등록
public class MemberServiceImpl implements MemberService{
...
}
@Component("service") // service 이름으로 컴포넌트 등록
public class OrderServiceImpl implements MemberService{
...
}
@Configuration
@ComponentScan
public class AppConfig {
}
이후 위 코드를 테스트를 수행하면 중복으로 인한 예외가 발생한다.
public class AppConfigTest {
@Test
void componentScanRun() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = ac.getBean("memberService", memberService.class);
}
}
수동 빈 등록과 자동 빈 등록의 충돌
하지만, 수동 빈 등록과 자동 빈 등록의 경우 수동 빈 등록이 우선권을 가지게 되어 자동으로 등록된 빈을 오버라이딩 한다.
자동 빈 등록
@Component // 기본적으로 빈 이름이 memoryMemberRepository로 지정됨
public class MemoryMemberRepository implements MemberRepository {
...
}
수동 빈 등록
public class AppConfig {
@Bean(name = "memoryMemberRepository") // 수동으로 빈 등록시 중복된 이름 설정
MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
이후 위 코드를 테스트하면 예외가 발생하지 않는다.
public class AppConfigTest {
@Test
void componentScanRun() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = ac.getBean("memberService", memberService.class);
}
}
...Overriding bean definition for bean 'memoryMemberRepository' with a different
definition: replacing...
하지만, 의도적으로 중복 등록하는 경우는 드물다. 대부분 여러 설정들이 복잡하게 꼬여서 이러한 결과를 만들게 된다.
이는 해결하기 어려운 버그가 만들어지는 원인이 되기 때문에, 스프링 부트에서는 수동 빈 등록과 자동 빈 등록이 일어나면 충돌 오류가 발생하도록 하여 중복 등록을 방지하고 있다.
'프레임워크(Framework) > Spring' 카테고리의 다른 글
[Spring] 스프링 조회한 빈(Bean)이 2개 이상인 경우 문제 해결 방법 (1) | 2022.11.07 |
---|---|
[Spring] 롬복(Lombok)을 이용한 의존관계 주입 방법 (0) | 2022.11.06 |
[Spring] 스프링 싱글톤 컨테이너 (웹 애플리케이션과 싱글톤) (0) | 2022.11.04 |
[Spring] 스프링 의존성 주입(DI : Dependency Injection) 4가지 방법 (의존 관계 자동 주입) (1) | 2022.11.03 |
[Spring] 자바 기반 컨테이너 설정 - @Bean과 @Configuration (0) | 2022.11.02 |