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`