설정 정보 없이 자동으로 스프링 빈을 등록
<bean id="orderService" class="hello.core.order.OrderServiceImpl">
<constructor-arg name="memberRepository" ref="memberRepository" />
<constructor-arg name="discountPolicy" ref="discountPolicy" />
</bean>
<bean id="discountPolicy" class="hello.core.discount.RateDiscountPolicy" />
<bean id="memberRepository" class="hello.core.member.MemoryMemberRepository" />
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
@ComponentScan
이라는 기능을 제공@Autowired
기능도 같이 제공ComponenetScan을 사용하면 @Component
어노테이션이 붙은 클래스를 스캔해 스프링 빈으로 등록
@Configuration
이 붙어 있으면 컴포넌트 스캔의 대상이 된다
@Configuration
에 @Componenet
어노테이션이 붙어 있기 때문이다. @Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
...
}
@Autowired
를 사용해서 생성자에 여러 의존관계도 한번에 주입받을 수 있다.@Component
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;//추상화에만 의존, DIP를 지키킨다.
@Autowired //의존관계 자동 주입 (ac.getBean(MemberRepository.class)와 같은 동작을 한다.)
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
@Configuration
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {}
@Test
@DisplayName("basicScan")
void basicScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
@ComponentScan(basePackage = "hh.core")
basePackage = {"hh.core", "hh.test"}
이렇게 여러 시작 위치를 지정할 수도 있다.@ComponentScan
이 붙은 설정 정보 클래스의 패키지가 시작위치가 된다.@ComponentScan
를 붙인 클래스가 속한 패키지인 package hh.core
가 된다 package hh.core;
@Configuration
@ComponentScan(
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
패키지 위치를 지정하지 않고, 설정 정보 클래스의 위치를 프로젝트 최상단에 두는것을 권장
@SpringBootApplication
를 이 프로젝트 시작 루트 위치에 두는 것이 관례이다. (그리고 이 설정안에 바로 @ComponentScan 이 들어있다!) @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
@Component
뿐만 아니라 다음과 내용도 추가로 대상에 포함@Component
: 컴포넌트 스캔에서 사용@Controlller
: 스프링 MVC 컨트롤러에서 사용@Service
: 스프링 비즈니스 로직에서 사용@Repository
: 스프링 데이터 접근 계층에서 사용@Configuration
: 스프링 설정 정보에서 사용@Component
public @interface Controller {
}
@Component
public @interface Service {
}
@Component
public @interface Configuration {
}
@Controller
: 스프링 MVC 컨트롤러로 인식@Repository
: 스프링 데이터 접근 계층으로 인식하고, 데이터 계층의 예외를 스프링 예외로 변환해준다. @Configuration
: 스프링 설정 정보로 인식하고, 스프링 빈이 싱글톤을 유지하도록 추가 처리를 한다.@Service
: @Service 는 특별한 처리를 하지 않는다.includeFilters
: 컴포넌트 스캔 대상을 추가로 지정excludeFilters
: 컴포넌트 스캔에서 제외할 대상을 지정public class ComponentFilterAppConfigTest {
@Test
@DisplayName("filterScan")
void filterScan(){
ApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
Assertions.assertThat(beanA).isNotNull();
assertThrows(NoSuchBeanDefinitionException.class,
()->ac.getBean("beanB", BeanB.class)
);
}
@Configuration
@ComponentScan(
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig{
}
}
ConflictingBeanDefinitionException
예외 발생Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true
@ComponentSca
은@Component
가 붙은 모든 클래스를 스프링빈으로 등록한다. 이때 스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자로 사용@Component("memberService2")
를 사용해서 이름을 부여한다.@Autowired
를 지정한 스프링컨테이너가 자동으로 해당 스프링 빈을 찾아서 주입한다