이제까지 컴포넌트 스캔을 통해서 설정 파일에서 따로 @Bean을 통해서 빈을 등록하지 않고
@Component을 통해 스프링 컨테이너에 등록하는 방법을 정리해보았다.
클래스에 @Component를 붙이고, 의존 관계 또한 해당 클래스 내에서 정의해줘야 하는데
이번엔 이 의존 관계 주입 방법에서 대표적인 4가지를 정리해보려 한다.
1. 생성자 주입
@Service
public class AService{
private final ARepository aRepository;
@Autowired
public AService(ARepository aRepository){
this.aRepository = aRepository;
}
}
- 말 그대로 생성자를 이용한 의존 관계 주입 방법이다.
- 스프링이 컴포넌트 스캔을 이용해서 클래스를 빈으로 등록하고, 호출 시점에 생성자가 딱 한번 호출되면서 의존 관계가 주입된다.
- 불변, 필수 의존 관계에 이용한다.
- 생성자가 만약 1개만 존재한다면 @AutoWired를 생략해도 자동 주입이 진행된다.
2. 수정자(setter) 주입
@Service
public class AService{
private ARepository aRepository;
@Autowired
public setARepository(ARepository aRepository){
this.aRepository = aRepository;
}
}
- Setter라 불리는 필드의 값을 변경하는 수정자 메서드를 이용해서 의존 관계를 주입하는 방법이다.
- 선택, 변경 가능성이 있는 의존 관계에 이용된다.
- 자바 빈 프로퍼티 규약의 수정자 메서드 방식을 이용하는 것이다.
- 멤버 변수는 private으로 선언되어야 하고, 프로퍼티라고 부른다
- setter, getter가 있어야 하고, public으로 선언되어야 한다.
가 자바 빈 규약이다. 아마 필드 값을 프로퍼티라고 하고, 값을 수정하기 위해서 setter 메서드를 이용한다는 의미 같다.
+ 생성자 주입에서는 필드 값이 final을 붙였는데, 수정자 주입에선 final 키워드를 뺐는데..?
- 자바에서는 final 키워드를 변수에 붙이면 상수, 즉 변경 불가능한 값을 의미한다.
메서드에 붙이면 재정의가 불가능, 클래스에 붙이면 상속이 불가능하다는 의미이다.
자바에서는 상수를 선언과 동시에 값을 할당되게끔 하는데, 생성자에서 멤버 변수를 초기화해주는 경우 선언과 할당이 따로 떼어질 수 있다.
생성자라는 것은 클래스의 객체가 생성될 경우, 무조건 거쳐야 하는 단계이므로 상수에 값이 할당됨을 보장한다. 따라서 컴파일러가 오류를 내지 않는다.
그러나 수정자는 객체가 생성됨과 동시에 실행되지 않는다. 객체가 생성된 뒤, 수정자를 통해 값을 할당하는 것인데, 객체가 생성되고 나서 상수가 선언은 되었으나 언제 값이 초기화될지 컴파일러는 알지 못한다.
따라서 수정자로 변수를 할당할 경우 final 키워드를 사용하지 못한다.
3. 필드 주입
@Service
public class AService{
@AutoWired
private ARepository aRepository;
}
- 말 그대로 필드 값으로 주입하는 방법이다.
- 매우 간단해졌다. 그냥 필드 값 위에 @AutoWired를 붙이면 된다.
- 하지만 단점이 있는데, 이렇게 @AutoWired를 쓸 수 있는 컨테이너 환경이 아닌, 순수한 자바 환경에서 테스트를 위해서 실행할 경우 변경할 방법이 없다. (의존 주입이 자동으로 된 경우는 구체화된 클래스가 주입이 되지만)
- 따라서 결국엔 setARepository 메서드를 또 만들어야 하는 문제가 생긴다.
- 결론적으로, DI 컨테이너가 필수적이라고 볼 수 있다.
4. 일반 메서드 주입
@Service
public class AService{
private ARepository aRepository;
@AutoWired
public void init(ARepository aRepository){
this.aRepository = aRepository;
}
}
- 이것 역시, 말 그대로 일반 메서드로 주입받는 것이다.
- 한 번에 여러 필드를 주입받을 수 있다.
- 일반적으로는 잘 사용하지 않는다.
5. 결론(생성자 주입을 선택하자!)
1. 불변
- 대부분의 의존 관계는 한번 결정되고 나면 변경되는 경우는 드물다고 한다.
- 생성자 주입은 생성자를 통해서 객체 생성과 동시에, 딱 한번 의존 관계 정의가 보장된다.
- 수정자 주입의 경우 setter를 public으로 열어둬야 한다.
(누군가가 수정이 가능하다, 변경하면 안 되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다)
2. 누락
- 수정자 주입을 사용하고, 순수한 자바 코드를 이용한 테스트를 돌려보면
@Test
void serviceTest(){
AService aService = new AService();
aService.find();
}
아마 Null Point Exception이 발생할 것이다. 왜냐하면 setARepository() 메서드를 이용해서 의존 관계를 주입해야 했기 때문이다.
- 이와 비교해서, 생성자 주입의 경우 주입 데이터의 누락이 생길 경우, 컴파일 에러를 발생한다.
- 또한, 위에서 말했듯이 final 키워드를 사용할 수 있는데 만약 생성자 주입에서 필드의 초기화를 한 개 빼먹으면 final 키워드로 인해 초기화되지 않은 필드 하나에 대해서 컴파일 에러가 발생한다.
++ 롬복 라이브러리 이용
- 롬복 라이브러리를 이용하면 @RequiredArgsConstructor 기능이 있다.
이 어노테이션은 필드 중 final 키워드가 붙은 필드들을 모아서 생성자를 자동으로 만들어준다.
또한, 아까 생성자가 하나일 경우 @AutoWired를 생략할 수 있다고 하였으니 최종적으로 간단하게 바뀐다.
@RequiredArgsConstructor
@Service
public class AService{
private final ARepository aRepository;
모두 생략되고 필드만 남게 된다.
1. final 키워드가 붙은 필드를 이용해서 생성자 자동 생성
2. 생성자가 1개일 경우 AutoWired 생략 가능
//@Autowired
//public AService(ARepository aRepository){
// this.aRepository = aRepository;
//}
}
'개발 공부 > 스프링' 카테고리의 다른 글
[SpringBoot] Error - java.net.SocketTimeoutException at PlainSocketImpl (0) | 2022.03.12 |
---|---|
[SpringBoot] Error - Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured. (0) | 2022.02.04 |
스프링 - @Component와 컴포넌트 스캔 (0) | 2021.09.17 |
스프링 - 스프링 컨테이너, 싱글톤 (0) | 2021.09.17 |
스프링 - 의존 관계와 DI, Ioc 컨테이너 (0) | 2021.09.16 |