백앤드/SpringBoot + Java 스프링 기본

[SpringBoot + Java] 스프링 기본(Autowired, Component)

맏리믓 2023. 8. 5. 21:29

들어가며

- 인드런 김영한 님의 강의 '컴포넌트 스캔' 의 정리 입니다.

- 앞서 @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 이 들어 있어 추가 하지 않아도 된다고 합니다.