@ComponentScan
- 에플리케이션의 컴포넌트들을 찾아 빈으로 등록한다.
- 컴포넌트란, @Component 에너테이션이 붙은 클래스.
- @Configuration 할 클래스에 붙여준다.
@ComponentScan
@Configuration
public class AutoConfig {
}
이 컴포넌트 스캔이 있기 전에는 @Configuration 으로 지정한 클래스에 @Bean 에너테이션을 이용하여, 빈 등록과 의존성 주입을 했다.
하지만 @ComponentScan 을 이용한 빈 등록을 처리하면, 의존성 주입은 어디서 할까?
--> @Autowired 를 이용하여 @Component 에너테이션이 붙은 클래스에서 자동 주입을 한다.
수동 빈 설정과 같이 ApplicationContext 생성시 해당 설정 클래스를 넣어주면 빈을 꺼낼 수 있다.
@Test
void getBean() {
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoConfig.class);
ac.getBean(~);
}
컴포넌트 스캔과 의존관계 주입 과정
1. @ComponentScan
- @Component 가 붙은 모든 클래스를 스프링 빈으로 등록한다.
- 스프링 빈의 기본 이름은 클래스명을 사용한다. 단 가장 앞글자를 소문자로 치환한다.
- 생각해보자. 수동 방식의 빈 주입이 원래 존재했고, 이는 메서드이름을 이름으로 했었다.
- 메서드 이름은 보통 lowCamelCase 를 관례상 이용했다.
- 하지만 이 자동주입방식은 클래스 이름을 사용하는데, 클래스 이름은 upperCamelCase 가 관례다.
- 이 두 간극을 매꾸기 위해 앞글자를 소문자로 하지 않았을까 하는 추측을 해본다.
- 빈 이름을 직접 지정하고 싶다면, @Component("원하는이름") 이 가능하다.
2. @Autowired
- 생성자 주입 : 생성자에 @Autowired 를 붙이면 자동으로 스프링 빈을 찾아 주입한다.
- 기본 조회 전략 : 생성자 매개변수의 타입이 같은 빈을 찾는다.
스캔 위치 설정
@ComponentScan 에너테이션에는 basePackage 옵션을 줄 수 있다.
@ComponentScan(
basePackages = "com.example"
)
위와 같이 하면, 컴포넌트를 스캔하는 작업을 com.example 패키지의 하위는 모두 스캔 대상에 포함하는 것이다.
{} 를 이용하여 복수로 등록할 수 있다.
권장
이 옵션을 지정하지 않으면 기본 디폴트는 해당 @ComponentScan 에너테이션이 붙은 패키지의 하위들이다.
따라서 굳이 basePackages 옵션을 지정하지 않고, 해당 클래스를 에플리케이션 최상위에 두면 모든 클래스들을 스캔할 수 있다.
Springboot
스프링부트에서는 프로젝트를 만들면 @SpringBootApplication 이라는 에너테이션이 붙은 메인함수를 담은 클래스가 프로젝트 소스코드 최상단에 자동으로 생성된다.
@SpringBootApplication 에는 @ComponentScan 에너테이션이 포함되어 있다. 따라서 우리는 스프링부트를 사용하면 따로 @ComponentScan 을 할 필요가 없는 것이다. 그리고 최상단에 이 클래스가 위치하기에, 에플리케이션의 모든 클래스가 스캔의 대상이 된다.
스캔 대상
아래의 에너테이션이 붙은 클래스들이 빈 스캔 대상이다.
- @Component : 컴포넌트 스캔에 사용
- @Controller : 스프링 MVC 의 컨트롤러에 사용
- @Service : 특별한 처리는 없고 서비스라는 것을 알려준다.
- @Repository
- 이 에너테이션은 빈 스캔 대상을 지정하는 역할 외에도 아래와 같은 역할을 한다.
- DB 를 관리하는 대상은 엄청 많다. 하지만 DB 마다 발생할 수 있는 예외들은 구현체별로 다르다.
- 이 예외들은 서비스단까지 올라와서 처리하게 되는데, 이 예외들이 다 다르면 DB 구현체를 바꿔낄때마다 예외처리를 매번 해줘야할 것이다.
- 스프링의 @Repository 에너테이션은 이 DB 에서 발생할 수 있는 예외들을 추상화하여 스프링 예외로 변환해준다.
- 우리는 일관적인 예외처리를 할 수 있게 된다.
- @Configuration : 스프링 설정 정보에 사용. 스프링 빈이 싱글톤을 유지하도록 해준다. (CGLIB 를 이용한 바이트코드 조작)
지금까지 @Controller, @Service, @Repository 들이 @Component 를 상속한다 라고 알고 있었다.
하지만 에너테이션에는 따로 상속관계는 없다. 자바단이 아닌 스프링단에서 @Controller, @Service, @Repository 를 스프링 빈으로 인식하는 기능을 제공하는것이다.
필터
필터는 @ComponetScan 에 줄 수 있는 옵션이다. 컴포넌트 스캔의 대상을 명시적으로 지정하거나 제외할 수 있다.
- includeFilter : 기본 대상 패키지 외에 스캔 대상으로 추가 지정한다.
- excludeFilter : 기본 대상 패키지 중에 스캔 대상의 예외로 지정한다.
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = 에너테이션.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = 에너테이션.class)
)
위와 같은 옵션을 줄 수 있으며, classes 에 지정한 에너테이션이 붙은 클래스는 추가하거나 제외할 수 있는 것이다.
FilterType 열거형에는 여러가지 옵션이 존재하며, 위의 FilterType.ANNOTATION 는 특정 에너테이션이 붙었는가 를 따져서 처리하겠다는 뜻이다.
타입과 자식 타입을 검사하거나, 정규식으로 검사하거나의 다른 옵션들이 존재한다.
충돌
수동 빈 등록, 자동 빈 등록이 생기며 충돌이 발생할 수 있어졌다.
충돌이란, 스프링 빈 컨테이너에 같은 이름의 빈이 두개가 있어서 발생하는 에러이다.
수동 빈 등록에서 beanA 라는 이름으로 등록했는데, 자동 빈 등록에서 또 beanA 를 등록하려는 그런 상황이다.
1. 수동 빈 등록 vs 수동 빈 등록
- 이런건 없다.
- 똑같은 메서드를 같은 클래스에서 지정할 수 없다.
2. 자동 빈 등록 vs 자동 빈 등록
- @Component("~") 식으로 등록하면 빈 이름을 지정할 수 있다고 했다.
- ConflictingBeanDefinitionException 예외가 발생한다.
- 그러니까 그냥 기본 옵션으로 지정해주는 이름을 쓰자.
3. 수동 빈 등록 vs 자동 빈 등록
- 협업시 가장 발생 할 확률이 높다.
- 이 경우 수동 빈 등록이 우선권을 가진다.
- 수동 빈 등록이 자동 빈을 오버라이딩한다.
- 이는 스프링의 기본 정책이다.
- 하지만 이렇게 오버라이딩해버리는 것은 로그에서 볼 수 있지만, 에러가 나지 않는다.
- 따라서 개발자가 자동 빈 등록한 빈을 사용하기로 의도했다면, 왜 의도한대로 동작하지 않는지 알기가 어렵다.
- 잡기 어려운 애매한 버그이다.
- 따라서 최근 스프링 부트(스프링아님) 에서는 이런 충돌이 발생하면 에러를 뱉고 프로그램을 멈춰버린다.
'spring' 카테고리의 다른 글
스프링 빈의 생명주기와 초기화 분리 (0) | 2021.03.20 |
---|---|
자동 주입시 빈이 2개 이상일 때 문제 해결 (0) | 2021.03.19 |
생성자 자동 주입의 장점, @RequiredArgsConstructor (0) | 2021.03.19 |
@Autowired (0) | 2021.03.18 |
DTO 는 어떻게 써야하나 (0) | 2021.03.15 |