Skills/Spring

Spring - ComponentScan(컴포넌트 스캔)

aoaa 2022. 9. 6. 23:47

1. ComponentScan

 Spring Bean을 등록할 때 @Bean  어노테이션을 이용하여 설정 정보에 직접 등록할 스프링 빈을 나열했습니다. 이 때, 등록해야 할 스프링 빈이 많을 경우에는 설정 정보도 커지고 누락하는 문제가 발생하게 되는데 이러한 문제를 해결하기 위해 Spring은 설정 정보가 없어도 자동으로 스프링 빈을 등록해주는 컴포넌트 스캔이라는 기능과 의존 관계를 주입하는 @Autowired 기능을 제공합니다.

 컴포넌트 스캔을 하게 되면 AppConfig에서 해주었던 빈 등록을 하지 않아도 된다. 대신 @Component 어노테이션이 붙은 클래스를 스캔하여 Spring Bean으로 등록하게 됩니다.

@Configuration
@ComponentScan
public class AutoAppConfig {

}

 먼저 Spring Container에 Bean을 등록할 클래스(AppConfig에서 수동 등록했던 빈)에 @Conponent 어노테이션을 붙여줍니다.

 

@Component
public class MemoryMemberRepository implements MemberRepository {}

@Component
public class RateDiscountPolicy implements DiscountPolicy {}

@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에서 수동으로 빈 등록을 할 때에는 의존 관계도 직접 명시했었지만, @Component 애노테이션으로 등록한 뒤, @Autowired를 이용해 의존 관계 주입을 해줍니다.

 

 




2. 컴포넌트 스캔 관례

 컴포넌트 스캔은 해당 위치부터 하위 패키지를 모두 스캔합니다. 스캔을 시작하는 패키지 위치를 지정해줄 수 있지만 일반적으로 메인 설정 정보 클래스의 위치를 프로젝트 최상단에 둡니다. 또한 Spring boot 대표 시작정보인 @SpringBootApplication을 프로젝트 시작 루트 위치에 두는 것이 관례입니다.

 


3. 스캔 기본대상 및 필터 

3.1 기본 대상

@Component : 컴포넌트 스캔에서 사용

@Controller : 스프링 MVC 컨트롤러에 사용, 스프링 MVC 컨트롤러로 인식

@Service : 스프링 비즈니스 로직에서 사용

@Repository : 스프링 데이터 접근 계층에서 사용, 스프링 데이터 접근 계층으로 인식, 데이터 계층의 예외를 스프링 예외로 변환해준다.

@Configuration : 스프링 설정 정보에서 사용, 스프링 설정 정보로 인식, 스프링 빈이 싱글톤으로 유지하도록 추가 처리

 

3.2 필터

  • includeFilters : 컴포넌트 스캔 대상을 추가로 지정
  • excludeFilters : 컴포넌트 스캔에서 제외할 대상을 지정
package hello.core.scan.filter;
import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyComponent {

}

@MyIncludeComponent
public class BeanA {

}

 

@ComponentScan(
	includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent.class),
//	excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent.class)
 )
public class ComponentFilterAppConfig {
 
 }

 

 IncludeFilter를 이용하면 스캔 범위에 없는 해당 클래스(BeanA)를 컴포넌트 스캔을 할 수 있고, excludeFilter를 이용하면 스캔 범위에 있는 해당 클래스(BeanA)를 컴포넌트 스캔을 하지 않도록 제외할 수 있습니다.

 


4. Bean의 중복과 처리

 같은 빈 이름으로 @Component를 이용하여 2번 등록하거나, 자동 빈 등록으로 같은 이름의 빈을 2번 등록할 시

ConflictingBeanDefinitionException 예외 발생하게 됩니다. 


 같은 빈 이름으로 @Component와 @Bean을 이용하여 2번 등록
자동 1번, 수동 1번으로 같은 이름의 빈을 2번 등록할 시, 수동으로 등록 한 빈이 우선권을 가져 수동 빈이 자동 빈을 오버라이딩 합니다. 
 하지만 이와 같은 경우는 잡기 어려운 버그가 만들어지므로 스프링 부트에서는 오류를 발생시키도록 변경이 되었습니다.

// 자동 등록
@Component
public class MemoryMemberRepository implements MemberRepository {}


@Configuration
@ComponentScan
public class AutoAppConfig {
	// 수동 등록
	@Bean(name = "memoryMemberRepository")
	public MemberRepository memberRepository() {
 		return new MemoryMemberRepository();
	}
}

 

 

 

 

 

 

'Skills > Spring' 카테고리의 다른 글

Spring - Bean Scope 관리  (0) 2022.09.13
Spring - Bean 생명주기 콜백  (0) 2022.09.09
Spring - Singleton(싱글톤)  (0) 2022.09.04
Spring - BeanFactory & BeanDefinition  (0) 2022.09.01
Spring - DI & Spring Container  (0) 2022.08.31