ComponentScan & AutoWired
@ComponentScan 이라는 기능을 제공한다.
@Component Annotation이 붙은 Class를 스캔하여 자동등록한다.
또, 의존관계도 자동으로 주입하는 @Autowired 기능도 제공한다.
Class 의 Constructor 위에 @Autowired Annotation을 붙여주면 된다.
ComponentScan 생성
package hello.core;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
@Configuration
@ComponentScan(
excludeFilters =
@ComponentScan.Filter(type= FilterType.ANNOTATION,classes = Configuration.class)
//(테스트를 위해 기존에 만들어둔) 직접 Bean 등록한 Component class 제외했음.
)
public class AutoAppConfig {
}
Component & Autowired 설정
package hello.core.member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component // @Component 어노테이션 삽입
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
@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);
}
public MemberRepository getMemberRepository() {
return memberRepository;
}
}
TEST 성공
package hello.core.scan;
import hello.core.AutoAppConfig;
import hello.core.member.MemberService;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AutoAppConfigTest {
@Test
void basicScan(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AutoAppConfig.class);
MemberService memberService = ac.getBean(MemberService.class);
Assertions.assertThat(memberService).isInstanceOf(MemberService.class);
}
}
탐색위치/기본스캔대상
basePackages = "/path/.packagename"(예시)
->탐색할 패키지의 시작위치를 지정한다. 이 패키지를 포함하여 하위 패키지를 모두 탐색한다.
(설정하지 않으면 모든 자바코드를 스캔한다.)
basePackageClasses = AutoAppConfig.class(예시)
->지정한 클래스의 패키지를 탐색 시작 위치로 지정한다.
(설정하지 않으면 @ComponentScan이 붙은 설정정보클래스가 패키지의 탐색시작위치가 된다.)
Spring Boot에서 제공하는 방식 -> 패키지 위치를 지정하지 않고, 설정정보 클래스의 위치를 프로젝트 최상단에 둔다.
예시:
com.hello.service
com.hello.repository ..
com.hello가 프로젝트 시작루트이므로, 여기에 AppConfig같은 메인설정정보를 두고
@ComponentScan 을 붙이고 basePackages는 생략.
이렇게 하면 com.hello 를 포함한 하위패키지는 모두 자동으로 컴포넌트 스캔의 대상이 된다.
ComponentScan 기본 Scan 대상
| 기본스캔대상 | 설명 | 부가기능 |
|---|---|---|
| @Component | ComponentScan에서 사용 | - |
| @Controller | Spring MVC Controller에서 사용 | Spring MVC Controller로 인식 |
| @Service | Spring Business Logic에서 사용 | - |
| @Repository | Spring 데이터 접근 계층에서 사용 | Spring Data 접근 계층으로 인식하고 데이터 계층의 예외를 스프링 예외로 변환 |
| @Configuration | Spring 설정정보에서 사용 | 스프링설정정보로 인식, 스프링 빈이 싱글톤 유지하도록 추가처리 |
참고
사실 Annotation에는 상속관계라는 것이 없다.
그래서 Annotation이 특정 Annotation을 상속하는 것처럼 동작하는 것은
Java 언어가 지원하는 기능이 아니고 Spring에서 지원하는 기능이다.
Filter
includeFilters -> CompnentScan 대상을 추가로 지정
excludeFilters -> CompnentScan 대상에서 제외
| Filter Options | |
|---|---|
| ANNOTATION | 기본값,Annotation을 인식해 동작 e.g) org.example.SomeAnnotation |
| ASSIGNABLE_TYPE | 지정한 타입과 자식 타입을 인식해 동작 e.g) org.example.SomeClass |
| ASPECTJ | AspectJ pattern 사용 e.g) org.example..*Service+ |
| REGEX | 정규표현식 e.g) org.example.Default.* |
| CUSTOM | ‘TypeFilter’인터페이스 구현해 처리 e.g) org.example.MyTypeFilter |
예시
@Configuration
@ComponentScan(
includeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyIncludeComponent(예시)).class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = MyExcludeComponent(예시)).class)
)
static class ComponentFilterAppConfig {
}
참고
@Component로 충분하므로 includeFilters를 사용할 일은 거의 없다.
excludeFilters는 간혹 사용되긴 하나 빈도가 많지는 않다.
Spring Boot는 ComponentScan을 기본으로 제공하는데, 옵션을 변경하며 사용하기 보다는
Spring의 기본설적에 맞추어 사용하는 것이 권장된다.
중복 Bean 등록과 충돌
1. 자동 빈 등록 vs 자동 빈 등록
그 이름이 같은 경우 스프링은 오류를 발생시킨다.
`ConfilictingBeanDeftinitionException`예외 발생
2. 수동 빈 등록 vs 자동 빈 등록
(수동등록 Bean이 자동등록 Bean을 Overriding 한다) Log : `Overriing bean definition for bean 'memoryMemberRepository'
with a different definition : replacing
최근 Spring Boot 에서는..
error:
`Consider renaming one of the beans fr enabling overriding
by setting spring.maim.allow-bean-definition-overriding=true`