의존성 주입 방식
의존성을 주입하는 방법은 4가지가 있다.
1. 생성자 주입(Constructor Injection)
2. setter
3. 필드 주입
생성자 주입
생성자를 통해 의존관계를 주입하는 방법이다.
@RestController
public class AnimalController {
private final AnimalService animalService;
@Autowired
public AnimalController (AnimalService animalService) {
this.animalService = animalService;
}
@PostMapping("/animal")
public ResponseEntity<String> adoptAnAnimal(AdoptRequestDto requestDto) {
return animalService.adoptAnAnimal(requestDto);
}
}
- 생성자 호출 시점에 1번만 호출되는 것을 보장한다.
- 주입받은 객체가 변하지 않거나 반드시 객체의 주입이 필요할 경우 강제하기 위해 사용할 수 있다.
- 생성자가 1개만 존재하는 경우 @Autowired를 생략해도 자동주입이 된다.
- 주입받을 필드를 불변을 보장하는 final로 선언이 가능하다.
- NPE(NullPointException)을 방지할 수 있다.
수정자 주입
Setter를 통해서 의존관계를 주입한다.
생성자 주입과는 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.
@RestController
public class AnimalController {
private AnimalService animalService;
@Autowired
public void setAnimalService (AnimalService animalService) {
this.animalService = animalService;
}
@PostMapping("/animal")
public ResponseEntity<String> adoptAnAnimal(AdoptRequestDto requestDto) {
return animalService.adoptAnAnimal(requestDto);
}
}
- 선택과 변경 가능성이 있는 의존관계에 사용한다.
- 자바의 Setter로 수정자 메서드 방식을 사용한다.
- @Autowired가 없으면 컴파일은 되지만, 실행 시 NPE가 발생한다.
- Setter는 언제든지 변경하게될 위험이 있다.
- 한번 주입이 일어나면 변경이 일어날 가능성이 거의 없으므로 지양한다.
필드 주입
필드에 바로 의존관계를 주입하는 방법이다.
@RestController
public class AnimalController {
@Autowired
private AnimalService animalService;
@PostMapping("/animal")
public ResponseEntity<String> adoptAnAnimal(AdoptRequestDto requestDto) {
return animalService.adoptAnAnimal(requestDto);
}
}
- 코드가 간결해진다.
- 외부에서 변경이 불가능하여 테스트가 어려워진다.
- DI프레임워크가 없으면 아무것도 할 수가 없다.
- 애플리케이션의 실제 코드와 상관없는 특정 테스트를 하고 싶을 때 사용한다.
생성자방식을 사용해야 하는 이유
1. 순환 참조 방지
AnimalService
@Service
public class AnimalService {
private UserService userService;
public AnimalService(UserService userService) {
this.userService = userService;
}
public ResponseEntity<String> adoptAnAnimal(AdoptRequestDto requestDto) {
return new ResponseEntity<>("입양되었습니다.", HttpStatus.OK);
}
}
AnimalService에서 의존성을 필드주입 방식으로 주입해 주었다.
테스트를 위해 userSerivce에서는 사용하지 않지만 예시를 위해 넣어주었다.
UserService
@Service
public class UserService {
private AnimalService animalService;
public UserService(AnimalService animalService) {
this.animalService = animalService;
}
public ResponseEntity<String> createUser(CreateUserRequestDto requestDto) {
return new ResponseEntity<>("이용자 등록이 완료되었습니다.", HttpStatus.OK);
}
}
UserService 또한 필드 주입방식으로 AnmalService를 주입해 주었다.
실행하면 순환참조가 일어난다는 문구가 나오게 된다.
컴파일 단계에서 순환참조의 원인을 파악할 수 있다는 장점이 있다.
2. 테스트 코드 작성 용이
생성자 주입 적용 전
@ActiveProfiles("test")
@SpringBootTest
class AnimalServiceTest {
@Autowired
AnimalService animalService;
@Test
@DisplayName("동물 하나를 입양하면 \"입양되었습니다.\"의 응답이 나오며 HTTP.OK를 반환한다.")
public void adoptAnimal() {
// given
AdoptRequestDto requestDto = new AdoptRequestDto(AnimalType.FOX, "미호", "female");
// when
ResponseEntity<String> response = animalService.adoptAnAnimal(requestDto);
// then
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isEqualTo("입양되었습니다.");
}
}
생성자 주입 적용
@ActiveProfiles("test")
@SpringBootTest
class AnimalServiceTest {
@Test
@DisplayName("동물 하나를 입양하면 \"입양되었습니다.\"의 응답이 나오며 HTTP.OK를 반환한다.")
void adoptAnAnimal() {
// given
AnimalService animalService = new AnimalService();
AnimalController animalController = new AnimalController(animalService);
AdoptRequestDto adoptRequestDto = new AdoptRequestDto(FOX, "미호", "female");
// when
ResponseEntity<String> response = animalController.adoptAnAnimal(adoptRequestDto);
// then
assertThat(response.getBody()).isEqualTo("입양되었습니다.");
}
}
mock 객체를 생성하거나 Autowired 할 필요도 없어져 테스트코드 작성이 편해졌다.
3. 불변성(Immutability)
필드 주입과 수정자 주입은 필드에 final을 붙일 수 없다.
따라서 초기화 한 후 빈 객체가 변경될 수 있지만, 생성자 주입은 final을 선언하여
객체의 불변성을 보장받을 수 있다.
'자바 탐구' 카테고리의 다른 글
자바) List, Set, Map, HashMap의 특성 (0) | 2023.07.24 |
---|---|
스프링) 스프링 컨테이너(Spring Container) (0) | 2023.07.24 |
JPA) 영속성 컨텍스트 (0) | 2023.07.21 |
스프링) SpringSecurity - 11) Oauth2.0 네이버 로그인 완료 (0) | 2023.05.26 |
스프링) SpringSecurity - 10) Oauth2.0 페이스북 로그인 완료 (1) | 2023.05.26 |