Go, Vantage point
가까운 곳을 걷지 않고 서는 먼 곳을 갈 수 없다.
Github | https://github.com/overnew/
Blog | https://everenew.tistory.com/
티스토리 뷰
* 김영한님의 스프링 핵심원리 기본편 강좌를 수강하며 정리한 글입니다. *
이전 글에서는 싱글톤 패턴과 스프링 컨테이너의 원리에 대하여 정리했다.
컴포넌트와 스캔
스프링 빈은 설정 정보를 @Bean으로 일일이 등록을 해주어야 한다.
이를 편리하게 자동으로 등록해주는 것이 컴포넌트 스캔이다.
설정 정보 클래스에 @ComponentScan 어노테이션을 추가로 붙여주면,
해당 설정 정보를 사용해 스프링 컨테이너 생성 시 모든 클래스를 대상으로 컴포넌트 스캔이 진행된다.
이제 클래스가 컴포넌트 스캔의 대상이 되도록 @Component 어노테이션을 추가하면 빈으로 등록된다.
하지만 설정 정보에서는 의존관계가 설정되어 있었다.
예를 들어 MemberServiceImpl를 생성할 때는 memberRepository를 컨테이너에서 자동으로 주입받았다.
@Bean
public MemberService memberService(){
//memberRepository()를 주입받음
return new MemberServiceImpl(memberRepository());
}
클래스별로 컴포넌트 등록을 한다면 DI(의존성 주입)은 어떻게 해결해야 할까?
이를 해결해 주는 것이 바로 @Autowired 어노테이션이다.
MemberServiceImpl의 생성자에 @Autowired를 붙여주면 스프링 생성 시 의존관계가 자동 주입된다.
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
컨테이너가 의존관계를 주입하는 순서는 다음과 같다.
1. @ComponentScan 이 붙은 설정 정보로 컨테이너 생성
new AnnotationConfigApplicationContext(AutoAppConfig.class);
2. 모든 클래스를 확인하여 @Component 가 붙은 클래스를 빈으로 등록
이때 등록되는 빈의 이름은 class의 앞 글자만 소문자로 바꾸어 등록된다.
( Member -> member)
3. 생성자에 @AutoWired가 붙으면 스프링 컨테이너가 해당 빈을 찾아 자동으로 주입
따라서 싱글톤이 지켜진다.
스캔 위치 지정과 대상
@ComponentScan에는 여러 옵션도 적용 가능하다.
특히 스캔 대상 패키지는 basePackages = "PATH" 또는 {"PATH1", "PATH2" ... }로 설정 가능하다.
basePackageClasses = class 는 해당 class의 패키지를 기본 탐색 위치로 설정한다.
@Configuration
@ComponentScan( basePackages = "hello.core.member")
// member 패키지 하위의 class만 스캔됨
public class AutoAppConfig {
}
만약 스캔 위치를 지정하지 않는다면, @ComponentScan 설정 정보 클래스의 패키지가 탐색 위치가 된다.
관례적으로 탐색 위치 설정보단, 설정 정보 클래스를 루트 패키지에 두어 사용한다.
사실, 스프링 부트를 사용한다면 @SpringBootApplication에는 @ComponentScan이 적용되어 있다.
따라서 @SpringBootApplication 이 적용된 클래스의 패키지가 자동으로 스캔 위치로 설정된다.
컴포넌트 스캔은 @Component 가 적용된 클래스를 등록하는데
@Controller, @Service, @Repository, @Configuration 에도 @Component가 적용되어 있다.
따라서 위의 어노테이션 적용 시 자동으로 스캔의 대상이 된다.
사실 어노테이션은 순수 자바에서는 주석의 역할(메타 정보)로 사용되지만, 위와 같은 복잡한 기능이 도대체 뭔가 생각이 들 수 있다. 이는 스프링에서 지원하는 기능으로 서로 연결하여 부가 기능을 수행해 준다.
컴포넌트 스캔 필터
@Configuration
@ComponentScan(includeFilters = @ComponentScan.Filter(type = FilterType.옵션,
classes = 포함 시킬.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.옵션,
classes = 배제 시킬.class)
)
static class ComponentFilterAppConfig{
}
includeFilters는 빈으로 등록시킬 클래스를
excludeFilters는 빈 등록에서 배제시킬 클래스를 필터로 설정할 수 있다.
필터의 옵션으로는 5가 존재한다.
ANNOTATION: default 옵션으로 어노테이션을 인식
ASSIGNABLE_TYPE: 지정한 클래스 타입 인식
ASPECTJ: AspectJ 패턴 사용
REGEX: 정규 표현식
CUSTOM: 직접 filter 인터페이스 구현해 적용
하지만 기본 설정에 맞추어 진행하는 상황이 많다.
@Component("빈 이름")으로 이름 설정도 가능하다.
단, 중복된 이름이 있다면 컴포넌트 스캔 시 다음 오류가 발생한다.
ConflictingBeanDefinitionException: Annotation-specified bean name
하지만 @Bean과 @Component 간의 이름 중복은 수동 빈(@Bean)이 자동 빈(@Component)을 오버라이딩 하여 오류가 발생하지 않는다.
물론, 오류 발생 없이 오버라이딩이 되어 버리면 개발자는 어떤 문제가 일어났는지도 알기 힘들 것이다.
따라서 스프링 부트는 오류를 발생시키도록 변경되었다.
'개발 > Spring' 카테고리의 다른 글
[Spring] 10. 스프링 빈의 라이프 사이클 (0) | 2022.06.30 |
---|---|
[Spring] 9. 의존 관계 주입 (0) | 2022.06.29 |
[Spring] 7. 싱글톤 컨테이너 (0) | 2022.06.26 |
[Spring] 6. 스프링 컨테이너 설정 정보와 BeanDefinition (0) | 2022.06.25 |
[Spring] 5. 스프링 빈 조회, getBean() (0) | 2022.06.25 |