void_melody 2022. 7. 13. 01:40

지금까지 @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로 수동 설정을 해주었다.

 

결론만 말하자면, 수동 빈 등록이 우선권, 즉 오버라이딩을 해버린다.

하지만 우리가 의도적으로 오버라이딩을 하는 경우보단 실수로 설정이 꼬여서 생겨버리는 경우가 훨씬 많다.

그래서 스프링부트에선 최근에 수동 빈 등록과 자동 빈 등록이 충돌나면 오류값이 나도록 기본값을 바꿨다고 하니 참고하자.

 

* 참고 : https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8/dashboard

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com