
컴포넌트 스캔과 @Autowired — 이제는 내가 안 만들고, 스프링이 만든다
지금까지는 이렇게 했다.
솔직히 말해서, 객체가 몇 개 안 될 때는 괜찮다.
근데 수십, 수백 개 되면?
설정 클래스가 지옥이 된다.
그래서 등장한 게 컴포넌트 스캔 + 자동 의존관계 주입이다.
기존 방식:
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
이제는 이런 설정을 안 써도 된다.
@Component
public class MemberServiceImpl implements MemberService {
}
그리고 설정 클래스에
@ComponentScan
만 붙이면 끝이다.
스프링이 알아서:
이 부분이 재밌다.
@Configuration 안에 들어가보면
실제로 @Component가 붙어있다.
즉, @Configuration도 결국 @Component의 일종이다.
그래서 컴포넌트 스캔을 켜면
이전 AppConfig 같은 설정 클래스도 같이 등록된다.
그래서 예제에서는 excludeFilters로 기존 설정 클래스를 제외했다.
이제 AppConfig가 없다.
그럼 의존관계는 어디서 연결하냐?
정답은 생성자에서 @Autowired.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
스프링이:
이건 내부적으로
getBean(MemberRepository.class
를 자동으로 해주는 거라고 보면 된다.
컴포넌트 스캔은 기본적으로
설정 클래스가 위치한 패키지부터 하위 패키지 전체를 스캔한다.
그래서 권장 패턴은:
스프링 부트에서는 이게 자동이다.
@SpringBootApplication
이 안에 이미 @ComponentScan이 들어있다.
실무에서는 거의 설정 위치를 따로 지정 안 한다.
이유는?
이 애노테이션들 안에 전부 @Component가 들어있기 때문이다.
⸻
단순히 스캔용이 아니다.
즉, 스프링은 애노테이션을 “의미”로도 사용한다.
includeFilters, excludeFilters로
이런 것도 가능하다.
근데 솔직히 말하면
: 실무에서 거의 안 쓴다.
요즘은
같은 이름이면 바로 예외 발생
ConflictingBeanDefinitionException
이건 오히려 안전하다.
⸻
예전 스프링
: 수동 빈이 자동 빈을 덮어씀
이게 진짜 무섭다.
구분이 안 된다.
그래서 최근 스프링 부트는
수동 + 자동 충돌 시 기본적으로 오류 발생
이게 훨씬 낫다.
컴포넌트 스캔은 단순히 “자동 등록”이 아니라
객체 생성 책임을 설정 클래스(AppConfig)에서
“애노테이션 기반 구조”로 이동시킨 것
그리고 @Autowired는
“이제 내가 new 안 하고, 스프링이 연결해준다” 선언
컴포넌트 스캔은
설정 코드를 줄이는 기능이 아니라, 구조를 애노테이션 중심으로 바꾸는 전환점이다.
출처
스프링 핵심 원리 - 기본편 (김영한, 인프런, 2020)