지금까지 스프링 빈과 의존관계를 모두 자바 코드의 @Bean 이나 XML 의 <bean> 등을 통해서 설정 정보에 직접 등록해주었다.
이제 스프링에서 제공하는 자동으로 빈을 등록해주는 @Component 기능을 사용해보자.
AppConfig.java
@Configuration
public class AppConfig {
@Bean
public MemberService memberService(){
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemoryMemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
System.out.println("AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
AutoAppConfig.java
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
Configuration.class))public class AutoAppConfig {
}
- 컴포넌트 스캔을 사용하려면
@ComponentScan을 설정 정보에 붙여주면 된다.- 기존의 AppConfig.java 와는 다르게
@Bean으로 등록한 클래스가 하나도 없다.
이제 각 클래스가 컴포넌트 스캔의 대상이 되도록 @Component 를 붙여주자.
MemoryMemberRepository @Component 추가
@Component
public class MemoryMemberRepository implements MemberRepository {}
RateDiscountPolicy @Component 추가
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
MemberServiceImpl @Component, @Autowired 추가
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
OrderServiceImpl @Component, @Autowired 추가
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicydiscountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}

@ComponentScan은@Component가 붙은 모든 클래스를 스프링 빈으로 등록한다.- 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다.
- 만약 스프링 빈의 이름을 직접 지정하고 싶으면@Component("Beanname")이렇게 하면 된다.

- 생성자에
@Autowired를 지정하면 자동으로 해당 스프링 빈을 찾아 주입한다.- 기본 조회 전략은 타입이 같은 빈을 찾아서 주입한다. (
getBean(MemberRepository.class)와 동일하다)
모든 자바 클래스를 다 컴포넌트 스캔 한다면 낭비가 심하다. 그래서 꼭 필요한 위치부터 탐색하도록 시작 위치를 지정할 수 있다.
@ComponentScan(
basePackages = "hello.springcore",
}
basePackages: 탐색할 패키지의 시작 위치를 지정.
-basePackages = { "hello.springcore", "hello.service" }여러 시작 위치를 정할 수 있다.basePackageClasses: 지정한 클래스의 패키지를 탐색 시작 위치로 지정.- 만약 지정하지 않으면
@ComponentScan이 붙은 설정 정보 클래스의 패키지가 시작위치가 된다.
컴포넌트 스캔은 @Componenet 뿐만 아닌 아래 내용도 추가로 대상에 포함된다.
@Controller : 스프링 MVC 컨트롤러에서 사용@Service : 스프링 비즈니스 로직에서 사용@Repository : 스프링 데이터 접근 계층에서 사용@Configuration : 스프링 설정 정보에서 사용includeFilters : 컴포넌트 스캔 대상을 추가로 지정.excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정.예제로 확인해 보자.
컴포넌트 스캔 대상에 추가할 애노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
컴포넌트 스캔 대상에서 제외할 애노테이션
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyIncludeComponent {
}
컴포넌트 스캔 대상에 추가할 클래스
@MyIncludeComponent
public class BeanA {
}
컴포넌트 스캔 대상에 제외할 클래스
@MyExcludeComponent
public class BeanB {
}
설정 정보와 테스트 코드
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
Assertions.assertThrows( NoSuchBeanDefinitionException.class,
() -> ac.getBean("beanB", BeanB.class));
}
@Configuration @ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes =
MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {}
}
빈 등록이 두 번 된다면 어떻게 될까?
@Component
public class MemoryyMeberRepository implements memberRepositroy {}
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
Configuration.class)
)
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
이 경우 수동 빈이 자동 빈을 오버라이딩 해버린다.
하지만 최근 스프링 부트에서는 이 경우 아래와 같은 오류를 낸다.
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
<출처 : 스프링 핵심 원리 - 기본편 by 김영한>