
@Bean, XML의 <bean>을 사용하여 등록하였다.@Autowired 기능을 제공한다.@Configuration
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {}
AppConfig 클래스를 수정하지 않고 새로운 클래스를 선언하였다.@ComponentScan을 설정정보에 추가한다.AppConfig와 달리 @Bean이 존재하지 않는다.@Component 어노테이션이 붙은 클래스를 스캔하여 스프링 빈으로 등록한다.@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration { ... }
@Configuration의 내부를 보면, @Component 어노테이션이 존재하는 것을 확인할 수 있다.excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
@Configuration 어노테이션이 붙어있다.excludeFilters를 사용하였다.@Component
public class MemoryMemberRepository implements MemberRepository {...}
@Component
public class RateDiscountPolicy implements DiscountPolicy {...}
@Component를 추가해준다.@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
...
}
@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;
}
...
}
AppConfig에서는 @Bean 으로 직접 설정 정보를 작성했고, 의존관계도 직접 명시했다.@Autowired를 사용해 자동으로 주입해준다.@Autowired 를 사용하여 생성자에서 여러 의존관계도 한번에 주입받는다.@Test
void basicScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
AnnotationConfigApplicationContext를 사용하여 스프링 컨테이너를 생성한다.AutoAppConfig 클래스를 넘겨준다.16:22:04.759 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\SpringStudy\core\out\production\classes\hello\core\discount\RateDiscountPolicy.class]
16:22:04.765 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\SpringStudy\core\out\production\classes\hello\core\member\MemberServiceImpl.class]
16:22:04.767 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\SpringStudy\core\out\production\classes\hello\core\member\MemoryMemberRepository.class]
16:22:04.770 [main] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - Identified candidate component class: file [C:\SpringStudy\core\out\production\classes\hello\core\order\OrderServiceImpl.class]
...
16:22:04.994 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'autoAppConfig'
16:22:05.004 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'rateDiscountPolicy'
16:22:05.006 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memberServiceImpl'
16:22:05.034 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'memoryMemberRepository'
ClassPathBeanDefinitionScanner - Identified candidate component class
ClassPathBeanDefinitionScanner가 컴포넌트 클래스를 식별함을 띄워주며, 해당 클래스의 위치를 알려준다.DefaultListableBeanFactory - Creating shared instance of singleton bean XXX

@ComponentScan은 @Component가 붙은 모든 클래스를 스프링 빈으로 등록시킨다.
@Autowired 를 지정하면, 스프링 컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다.getBean(MemberRepository.class) 와 동일하다고 이해하면 된다.
@ComponentScan(
basePackages = "hello.core",
}
basePackages : 탐색할 패키지의 시작 위치를 지정한다. 이 패키지를 포함해서 하위 패키지를 모두 탐색한다.basePackages = {"hello.core", "hello.service"}
basePackageClasses : 지정한 클래스의 패키지를 탐색 시작 위치로 지정한다.@ComponentScan 이 붙은 설정 정보 클래스의 패키지가 시작 위치가 된다@Component : 컴포넌트 스캔에서 사용@Controlller : 스프링 MVC 컨트롤러에서 사용@Service : 스프링 비즈니스 로직에서 사용@Repository : 스프링 데이터 접근 계층에서 사용@Configuration : 스프링 설정 정보에서 사용@Component를 포함한다.@Controlller : 스프링 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 MyExcludeComponent {}
@MyIncludeComponent
public class BeanA {}
@MyIncludeComponent를 적용한다@MyExcludeComponent
public class BeanB {}
@MyExcludeComponent를 적용한다@Test
void filterScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
assertThat(beanA).isNotNull();
org.junit.jupiter.api.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{}
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
ComponentFilterAppConfig클래스를 설정 정보로 전달한다.@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig{}
ComponentFilterAppConfig클래스는 컴포넌트 스캔 방식으로 빈을 등록한다.@ComponentScan의 속성으로 includeFilters와 excludeFilters를 지정하였다.includeFilters에 MyIncludeComponent 애노테이션을 추가해서 BeanA가 스프링 빈에 등록된다.excludeFilters에 MyExcludeComponent 애노테이션을 추가해서 BeanB가 스프링 빈에 등록되지 않는다.@Component어노테이션이 포함된 클래스를 빈으로 등록하는데, includeFilters속성을 통해 빈으로 등록시켰다.@Component 면 충분하기 때문에, includeFilters를 사용할 일은 거의 없다. excludeFilters는 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않다컴포넌트 스캔에서 같은 이름을 가진 빈을 등록할 경우 문제가 발생한다.
@Component) vs 자동 빈 등록 (@Component)@Bean) vs 자동 빈 등록 (@Component)@Component) vs 자동 빈 등록 (@Component)@Component("service")
public class OrderServiceImpl implements OrderService{...}
@Component("service")
public class MemberServiceImpl implements MemberService{...}
OrderServiceImpl을 스프링 빈으로 등록할 때 service라는 이름으로 등록MemberServiceImpl을 스프링 빈으로 등록할 때 service라는 이름으로 등록... org.springframework.context.annotation.ConflictingBeanDefinitionException ...
ConflictingBeanDefinitionException 예외가 발생하게 된다.@Bean) vs 자동 빈 등록 (@Component)...
@ComponentScan
public class AutoAppConfig {
@Bean(name = "memoryMemberRepository")
MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
@Bean을 사용하여 빈을 등록할 경우, @Component에 의해 자동으로 등록된 빈과 중복이 발생한다.@Bean)이 우선권을 갖는다Overriding bean definition for bean 'memoryMemberRepository' with a different definition: ...
The bean 'memoryMemberRepository', defined in class path resource [hello/core/AutoAppConfig.class] could not be registered
A bean with that name has already been defined in file and overriding is disabled.
출처: 인프런 스프링 핵심 원리 - 기본편 (김영한)
인프런 스프링 핵심 원리