지금까지 @Bean을 붙여서 스프링 빈을 넣어주었다.
하지만 실무에서 이 Bean을 붙여줘야할 것들이 수십, 수백개가 된다면?
for문돌려서 할 수 있는 것도 아니잖아..
그렇기에 스프링에서는 설정 정보가 없어도 자동으로 스프링 빈을 등록하는 Component Scan 기능을 제공한다.
@Configuration
@ComponentScan
public class AutoAppConfig{
}
Component Scan을 사용하려면 @ComponentScan을 붙여주면 된다.
ComponentScan은 @Component이 붙은 클래스를 스캔해서 스프링 빈으로 넣어주는 기능을 한다.
(@Configuration 소스코드를 열어보면 @Component이 붙어있다. 즉, Configuration도 ComponentScan 대상이 된다.)
@Component
public class MemoryMemberRepository implements MemberRepository {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
}
이전에 AppConfig에서는 @Bean으로 직접 설정을 해줬고 거기 안에서 의존관계를 명시했다.
(ex. DiscountPolicy = RateDiscountPolicy;)
하지만 해당 코드에선 설정 정보 자체가 없어서, 의존관계 주입을 여기서 해결해줘야한다.
이러한 기능을 @Autowired가 알아서 해결해준다.
public class AutoAppConfigTest{
@Test
void basicScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
}
}
기존의 @Bean으로 해준거랑 지금의 ComponentScan을 해준 것이나 같은 결과가 나온다.
// 의존관계 설명
아까 언급했다시피
@ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.
이 때 스프링 빈의 기본이름은 클래스 이름이되 맨 앞글자만 소문자로 사용한다.
즉, 예시로 MemBerServiceImpl의 클래스를 스프링 빈으로 등록하면 Default로 "memberServiceImpl"로 등록된다.
만약 이름을 지정해주고 싶다면 @Component("지정하고싶은")이렇게 인자로 넣어주면 된다.
물론 이름을 지정해주는 일은 잘 활용하지는 않는다.
이제 생성자에 @Autowired를 지정하면, 스프링 컨테이너가 자동으로 스프링 빈을 찾아서 넣어준다.
// getBean(MemberRepository.class) 랑 동일하다.
생성자에 파라미터가 많아도 다 찾아서 자동으로 주입하겠지.
* 탐색 위치와 기본 스캔 대상
모든 자바 클래스를 다 Component Scan을 하면 시간이 오래 걸린다.
그렇기에, 꼭 필요한 위치부터 시작하도록 설정해줄 순 있다.
@ComponentScan(
basePackages = "hello.core" // 시작을 원하는 위치
}
만약 지정하지 않는다면, Default로 @ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다.
강사님이 권장하는 방법은
"패키지 위치를 지정하지 않고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두는 것이다."
예를 들어서 프로젝트가
com.hello
com.hello.service
com.hello.repository
로 되어있다면
com.hello 여기에 AppConfig 같은 메인 설정 정보를 두고, @ComponentScan을 붙이는 것이다.
이러면 com.hello를 포함한 모든 하위는 다 Component Scan의 대상이 된다.
SpringBoot에서는 실제로 이렇게 작동하고 있다.
ComponentScan의 기본 대상은 @Component뿐만이 아니라, 우리가 입문 강의에서 배웠던
@Controller
@Service
@Repository
@Configuration
에서도 적용된다.
해당 소스들을 열어보면 @Component를 가지고 있다.
*필터
- includeFilters : 컴포넌트 스캔 대상을 추가로 지정
- excludeFilters : 컴포턴트 스캔에서 제외할 대상을 지정
실무에선 실제로 @Component면 충분하기 때문에, includeFilters를 사용할 일은 없다.
excludeFilters의 경우 간혹 사용하긴 하지만 많진 않다.
* 중복 등록과 충돌
ComponentScan에서 같은 빈 이름을 등록하면? 당연히 충돌나겠지.
두 가지 경우의 수가 있다.
1. 자동 빈 등록 vs 자동 빈 등록
ComponentScan에 의해 자동으로 스프링 빈이 등록되는데, 이름이 같으면 스프링은 오류를 발생한다.
(ConflictingBeanDefinitionException)
어떻게 보면 당연하다. 무엇을 개발자가 원하는지 스프링은 모르지..
2. 수동 빈 등록 vs 자동 빈 등록
@Component
public class MemoryMemberRepository implements MemberREpository {}
@Configuration
@ComponentScan(
excludeFilters = @Filter(classes = Configuration.class)
)
public class AutoAppConfig{
@Bean(name = "memoryMemberRepository)
public MemberRepository memberRepository(){
return new MemoryMemberRepository();
}
}
보면 위의 @Component로 인해 MemoryMemberRepository -> memoryMemberRepository 라는 이름의 자동 빈 등록
@Bean에서 이름을 memoryMemberRepository로 수동 설정을 해주었다.
결론만 말하자면, 수동 빈 등록이 우선권, 즉 오버라이딩을 해버린다.
하지만 우리가 의도적으로 오버라이딩을 하는 경우보단 실수로 설정이 꼬여서 생겨버리는 경우가 훨씬 많다.
그래서 스프링부트에선 최근에 수동 빈 등록과 자동 빈 등록이 충돌나면 오류값이 나도록 기본값을 바꿨다고 하니 참고하자.
'스프링 강의 필기 > 스프링 핵심 원리 - 기본편' 카테고리의 다른 글
섹션 7 - (2). 의존관계 자동 주입 (0) | 2022.07.13 |
---|---|
섹션 7 -(1) 의존관계 자동 주입 (0) | 2022.07.13 |
섹션 5 - 싱글톤 컨테이너 (0) | 2022.07.09 |
섹션4 - 스프링 컨테이너와 스프링 빈 (0) | 2022.07.09 |
섹션 3. 스프링 핵심 원리 이해2 - 객체 지향 원리 적용 (0) | 2022.07.09 |