티스토리 뷰
*컴포넌트 스캔: 설정 정보가 없어도 스프링이 자동으로 스프링 빈을 등록하는 기능
//구성을 스프링으로 관리하기 위한 @Configuration
@Configuration
public class AppConfig {
//스프링 컨테이너에 객체들을 관리 하기 위해 @Bean으로 등록
//스프링 컨테이너에 등록된 객체들을 '스프링 빈' 이라고 한다
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
위 코드처럼 설정 정보를 구성하면 등록해야 하는 Bean이 늘어날 때 마다 하나하나 수동으로 추가해줘야 하고 개수가 늘어날수록 누락할 확률이 높아진다. 이런 문제점을 보완하기 위해 컴포넌트 스캔을 활용한다. 아래 코드와 같다.
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
// //컴포넌트 스캔의 시작지점을 정할 수 있고, 하위 패키지도 다 포함한다
// basePackages = "hello.core.member",
//
// //클래스를 지정하는 방식
// basePackageClasses = AutoAppConfig.class;
// 만약 위 처럼 범위를 지정하지 않으면, @ComponentScan이 붙은 클래스의 패키지가 시작 위치가 된다
// 여기서는 hello.core 부터 하위의 클래스들을 전부 스캔한다.
//컴포넌트 스캔을 통해 @Component가 붙은 클래스를 자동으로 빈으로 등록하는데 그 중에서 배제할 것을 정하는 것.
//만들어둔 TestConfig나 AppConfig에 @Configuration이 있는데 그것을 포함하면 거기서 수동으로 만들어
//포함되어 있는 스프링 빈들과 충돌나기 때문에 배제
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AutoAppConfig {
}
package hello.core.member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
//컴포넌트 스캔을 쓰게 되면 AutoAppConfig에서 의존 관계를 설정할 수 없기 때문에
//생성자 위에 의존 관계 자동 주입을 해주는 @Autowired를 추가해준다
//그러면 스프링 컨테이너가 자동으로 해당 클래스 타입에 해당하는 스프링 빈을 찾아서 주입한다
@Autowired
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
-AutoAppConfig 클래스에는 설정 정보가 없는 것을 확인할 수 있다. @ComponentScan 어노테이션을 이용하면 설정된 범위에서 @Component 가 붙은 클래스를 스프링 컨테이너가 스프링 빈으로 등록한다 (참고로 @ComponentScan은 다른 어노테이션도 스캔함). 빈의 이름은 등록된 클래스 명의 앞글자만 소문자로 한 것과 같다.
-원래는 설정 정보에서 수동으로 new를 통해 의존관계를 주입했지만, 컴포넌트 스캔을 이용하면 설정 정보가 비어 있으므로 빈으로 등록할 클래스의 생성자에 @Autowired를 붙여서 자동으로 의존 관계를 주입한다. 여러 개 주입도 한 번에 가능하다.
자동 관계 주입(여기서는 생성자 주입 방법)은 스프링 빈에 등록된 것 중 타입이 같은 것을 선택하여 가져와 주입한다. 위 코드에서는 MemberRepository 클래스 타입에 해당하는 것 중 스프링 빈에 등록된 것이 있으면 가져올 것이다 (여러 개 있어서 충돌 가능한 경우는 이 글 맨 아래에 적어놓았음)
아래 코드와 같이 filter 기능으로 스프링 빈에 포함할 클래스와 배제할 클래스를 정할 수 있다.
package hello.core.scan.filter;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.springframework.context.annotation.ComponentScan.*;
public class ComponentFilterAppConfigTest {
@Test
void filterScan() {
ApplicationContext 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 = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
)
static class ComponentFilterAppConfig {
}
}
하지만 정작 필터 기능을 많이 사용하지는 않는다고 한다. 스프링의 기본 설정에 맞추어 사용하는 것을 권장.
*컴포넌트 스캔 중 스프링 빈이 중복되어 충돌하는 경우
-만약 중복되는 빈 두개가 전부 자동 등록, 즉 @Component를 통해 생성되었다면 오류 메시지를 일으킨다.
-하지만 수동 등록(@Bean)과 자동 등록이 중복되면 수동 등록한 것이 우선되고 오버라이딩 되어 등록된다
--> 개발자가 의도하지 않은 경우가 대부분이고 Bean이 많을 수록 알 수 없는 에러가 발생할 수 있다
--> 따라서 최근 스프링부트는 이러한 경우를 처음부터 예방하기 위해 오류 메시지를 일으키도록 설정되어있다.
참고: 김영한 - 스프링 핵심 기본 원리(인프런)
'Spring' 카테고리의 다른 글
| 빈 생명주기와 콜백 (0) | 2023.08.17 |
|---|---|
| 롬북(Lombok)의 @RequiredArgsConstructor 기능 (0) | 2023.08.16 |
| @Configuration의 역할과 싱글톤 (0) | 2023.08.14 |
| 싱글톤 패턴과 스프링 컨테이너 (0) | 2023.08.14 |
| 좋은 객체 지향 설계 5가지 원칙(SOLID) (0) | 2023.08.09 |