[SpringBoot + Java] 스프링 기본(Autowired, Component)
들어가며
- 인드런 김영한 님의 강의 '컴포넌트 스캔' 의 정리 입니다.
- 앞서 @Bean 을 통한 스프링 빈의 관리에 대해 알아 보았습니다.
- 하지만 아주 큰 프로젝트에서 스프링 빈이 굉장히 많아 지면 어떨까요
- 설정 정보고 커지고 유지 보수가 어렵게 되고 무엇보다 실수 할 가능성이 커지게 됩니다.
- 이러한 문제를 ComponentScan 과 Autowired 로 해결 할 수 있습니다.
ComponentScan
- 위에 언급 한 것 처럼 너무 많은 Bean 을 수동으로 등록 하게 되면 문제가 발 생 할 수 있습니다.
- ComponentScan 은 Scan 대상 중 @Component 어노테이션이 달린 모든 class 를 자동으로 스프링 빈에 등록 해 줍니다.
ComponentScan 사용법
- 사용 법은 굉장히 간단 합니다.
. 아래와 같이 @Configuration, @ComponentScan 어노테이션만 달게 되면 됩니다.
//AppConfig
@Configuration
@ComponentScan
public class AppConfig{
}
- 위와 같이 하게 되면 AppConfig 파일이 있는 package 부터 시작 하여 @Component 가 붙은 class 들을 자동으로 스프링 빈에 등록 해 줍니다.
- 이 때 등록을 하지 않았으면 하는 게 있다면 Filter 를 사용 하면 됩니다.
. 아래는 기존의 자동 스캔을 사용 하지 않을 때 사용 했던 Configuration class 를 Bean 에 등록 하지 않기 위해 사용 함
//AppConfig
@Configuration
@ComponentScan(
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Configuration.class)
)
public class AppConfig{
}
- 또한 ComponentScan 을 통해 Scan 하는 시작점도 정해 줄 수 있습니다.
. 기본은 AppConfig 가 존재 하는 위치 부터 scan 을 실시 합니다.
. 이때 basePackages 를 통해 scan 의 시작점을 정해 줄 수 있습니다.
@ComponentScan(
basePackages = "~~.~~.~~",
)
Component 추가
- 위의 방식대로 Scan 을 사용 하기 위해선 자동으로 Bean 에 등록 되었스면 하는 class 를 Component 로 설정 해 주어야 합니다.
- 예를 들어 Repository 로 ARepository 를 사용 한다면 아래 코드와 같이 Component 어노테이션을 붙여 주어야 합니다.
@Component
public class ARepository implements Repository{
//...
}
Autowired
- 이제 Bean 에 등록을 하였으니 그것을 꺼내어 사용 하여야 합니다.
- 그때 사용 하는것이 Autowired 입니다.
- 아래 코드는 수동 DI 방식의 코드에 @Component 와 @Autowired 만 추가 한 것입니다.
. 이렇게 하기만 해도 자동 DI 방식의 AppConfig 를 사용 할 때 자동으로 repository 에 @Component 로 설정 해 두었던 ARepository 가 주입 되게 됩니다.
@Component
public class serviceImpl implememts Service{
private final Repository repository;
@Autowired
public ServiceImpl(Repository repository){
this.repository = repository;
}
//...
}
동작 순서
- 위의 글만 봐서는 이해가 잘 안될 수 있기 때문에 어떠한 순서로 동작 되는지 간단히 설명 해 보겠습니다.
1. @ComponentScan 을 통해 @Component 가 붙은 모든 class 를 스프링 빈에 등록 합니다.
- 이 때 빈의 기본 이름은 앞글자만 소문자로 변환 시켜 사용 합니다.
2. 생성자에 @Autowired 를 지정 하면 스프링 컨테이너가 자동으로 스프링 빈을 찾아 주입 해 줍니다.
- 이 때 생성자의 변수부분의 타입에 맞는 빈을 주입 합니다( 위 예시에선 Repository type 을 주입)
주입 시 중복 등록과 충돌
- 설명을 듣다보면 의아한 부분이 있을 수 있습니다.
- 앞선 부분 까지는 수동으로 어떠한 class 를 등록할 것인지 지정 해 주었지만 지금 같은 경우는 자동으로 등록 하기 때문에 같은 type 의 class 가 존재 한다면 어떻게 될까요
- 이러한 경우를 중복 등록이라 하는데 여기에는 2가지 종류가 있습니다.
. 자동 등록과 자동 등록의 충돌
. 자동 등록과 수동 등록의 충록
- 자동 vs 자동
. 이 경우는 Repository 를 상속 받은 ARepository 와 BRepository 가 모두 @Component 어노테이션이 달려 있으면 발생 할 수 있습니다.
. 이러한 경우에는 스프링에서 "ConflictingBeanDefinitionException" 예외를 발생 시킵니다.
- 자동 vs 수동
. 이 경우는 Repository 를 상속 받은 ARepository 와 BRepository 중 A 를 @Component 어노테이션을 달아 Scan 이 된 상황에서 수동으로 B 를 주입하면 발생 할 수 있습니다.
. 이러한 경우에는 수동 빈 등록 쪽이 우선권을 가져 B가 등록 되게 되고 아래 로그를 남깁니다.(B 가 A 를 override 해 버림)
"Overriding bean definition for bean 'memoryMemberRepository' with a different definition: replacing"
. 다만 이런 경우는 개발자가 의도한 경우가 아닐 가능성이 높을 뿐더러 원하지 않는 결과를 도출 할 수 있기 때문에 최신 스프링에서는 오류가 나도록 설정 해 두었다고 합니다.
+
- 근데 사실 프로그램의 시작점인 ~Application 에 달려 있는 @SpringBootApplication 에는 @ComponentScan 이 들어 있어 추가 하지 않아도 된다고 합니다.