@Component
public class MemberServiceImpl implements MemberService{
@Component
public class MemoryMemberRepository implements MemberRepository{
@Component
public class OrderServiceImpl implements OrderService{
위처럼 컴포넌트로 등록해놓으면 스프링이 컴포넌트 스캔을 진행해서 스프링 빈 저장소에 넣어준다.
@ComponentScan이라는 어노테이션이 붙은 설정파일의 패키지부터 그 하위패키지 전부를 스캔하게 되는데 사실 @SpringBootAplication을 통해 ComponentScan까지 진행되기 때문에 우리는 스프링만 잘 사용하면 된다.
근데 의존관계를 어떻게 주입할 수 있을까?
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
위와 같이 @Autowired로 DI를 진행하면 된다.
Autowired를 통해 해당 컴포넌트를 스프링 빈 저장소에서 찾아서 주입해준다.
구조는 아래와 같다.
위 단계들을 거쳐서 자동으로 의존관계 주입이 되고 싱글톤도 지키게 된다.
@MyIncludeComponent
public class BeanB {
}
@MyExcludeComponent
public class BeanA {
}
@Test
void filterScan() {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(ComponentFilterAppConfig.class);
BeanA beanA = ac.getBean("beanA", BeanA.class);
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 {
}
위 코드를 보면 @ComponentScan에서 include, excludeFilters를 사용해 제외할 컴포넌트를 고른다.
여기서 컴포넌트 어노테이션은 아래처럼 직접 제작했다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyExcludeComponent {
}
@ExcludeComponet로 등록된 객체는 스프링 빈에 등록되지 않는다.
org.example.SomeAnnotation
org.example.SomeClass
org.example..*Service+
org\.example\.Default.*
TypeFilter
이라는 인터페이스를 구현해서 처리 ex) org.example.MyTypeFilter
참고:
@Component
면 충분하기 때문에,includeFilters
를 사용할 일은 거의 없다.excludeFilters
는 여러가지 이유로 간혹 사용할 때가 있지만 많지는 않다.
특히 최근 스프링 부트는 컴포넌트 스캔을 기본으로 제공하는데, 개인적으로는 옵션을 변경하면서 사용하기 보다 는 스프링의 기본 설정에 최대한 맞추어 사용하는 것을 권장하고, 선호하는 편이다.
이 경우에는 ConflictingBeanDefinitionException 예외가 발생된다.
이 경우에는 수동 등록이 우선 순위를 갖게 된다.
물론 개발자가 의도적으로 이런 결과를 기대했다면, 자동 보다는 수동이 우선권을 가지는 것이 좋다. 하지만 현실은 개 발자가 의도적으로 설정해서 이런 결과가 만들어지기 보다는 여러 설정들이 꼬여서 이런 결과가 만들어지는 경우가 대 부분이다!
최근 스프링 부트에서는 자동 수동의 상황에서도 에러를 내도록 바뀌었다.