일단, 의존성 주입의 종류로는 Constructor
, Setter
, Field
타입이 있습니다.
public class ExampleCase {
private final ChocolateService chocolateService;
private final DrinkService drinkService;
@Autowired
public ExampleCase(ChocolateService chocolateService, DrinkService drinkService) {
this.chocolateService = chocolateService;
this.drinkService = drinkService;
}
}
public class ExampleCase{
private ChocolateService chocolateService;
private DrinkService drinkService;
@Autowired
public void setChocolateService(ChocolateService chocolateService){
this.chocolateService = chocolateService;
}
@Autowired
public void setDrinkService(DrinkService drinkService){
this.drinkService = drinkService;
}
}
public class ExampleCase{
@Autowired
private ChocolateService chocolateService;
@Autowired
private DrinkService drinkService;
}
필드 인젝션
방식을 보통 많이 사용하는데 필드 인젝션
은 아래와 같은 단점들이 있습니다.
1. 단일 책임의 원칙 위반
필드 주입 망식은 일단 의존성을 주입하기 쉽습니다. @Autowired
선언 아래 3개든 10개든 막 추가할 수 있으니... 여기서 Constructor Injection
을 사용하면 다른 Injection
타입에 비해 위기감 같은 걸 느끼게 해줍니다. Constructor
의 파라미터가 많아짐과 동시에 하나의 클래스가 많은 책임을 떠안는다는 걸 알게됩니다. 이때 이러한 징조들이 리팩토링을 해야한다는 신호가 될 수 있습니다.
2. 의존성이 숨는다.
DI(Dependency Injection)
컨테이너를 사용한다는 것은 클래스가 자신의 의존성만 책임진다는게 아닙니다. 제공된 의존성 또한 책임집니다. 그래서 클래스가 어떤 의존성을 책임지지 않을 때, 메서드나 생성자를 통해(Setter나 Contructor) 확실히 커뮤니케이션이 되어야만합니다. 하지만 Field Injection
은 숨은 의존성만 제공해줍니다.
3. DI 컨테이너의 결합성과 테스트 용이성
DI 프레임워크의 핵심 아이디어는 관리되는 클래스가 DI 컨테이너에 의존성이 없어야합니다. 즉, 필요한 의존성을 전달하면 독립적으로 인스턴스화 할 수 있는 단순 POJO여야합니다. DI 컨테이너 없이도 유닛테스트에서 인스턴스화 시킬 수 있고, 각각 나누어서 테스트도 할 수 있습니다. 컨테이너의 결합성이 없다면 관리하거나 관리하지 않는 클래스를 사용할 수 있고, 심지어 다른 DI 컨테이너로 전환할 수 있습니다.하지만, Field Injection을 사용하면 필요한 의존성을 가진 클래스를 곧바로 인스턴스화 시킬 수 없습니다.
4. 불변성(Immutability)
Constructor Injection
과 다르게 Field Injection
은 final
을 선언할 수 없습니다. 그래서 객체가 변할 수 있습니다.
순환 의존성
Constructor Injection
에서 순환 의존성을 가질 경우 BeanCurrentlyCreationExeption
을 발생시킴으로써 순환 의존성을 알 수 있습니다.
순환 의존성이란?
First Class가 Second Class를 참조하는데 Second Class가 다시 First Class를 참조할 경우 혹은 First Class가 Second Class를 참조하고, Second Class가 Third Class를 참조하고 Third Class가 First Class를 참조하는 경우 이를 순환 의존성이라고 부릅니다. (혹은 순환 참조)
@RequiredArgsConstructor
어노테이션을 사용한 생성자 주입 방법생성자 주입의 단점은 생성자를 작성하기 번거롭다는 것인데.. 롬복
을 사용하여 간단한 방법으로 생성자 주입 방식의 코딩을 할 수 있습니다.
@RequiredArgsConstructor
final
이 붙거나 @NotNull
이 붙은 필드의 생성자를 자동 생성해주는 롬복 어노테이션
필드 주입방식을 사용한 기존 Service
@Service
public class BannerServiceImpl implements BannerService {
@Autowired
private BannerRepository bannerRepository;
@Autowired
private CommonFileUtils commonFileUtils;
@RequiredArgsConstructor 를 활용한 생성자 주입
@Service
@RequiredArgsConstructor
public class BannerServiceImpl implements BannerService {
private final BannerRepository bannerRepository;
private final CommonFileUtils commonFileUtils;
...
@RequiredArgsConstructor
를 사용하지 않으면 원래는 이렇게 생성자 주입을 해야한다..
@Service
public class BannerServiceImpl implements BannerService {
private BannerRepository bannerRepository;
private CommonFileUtils commonFileUtils;
@Autowired
public BannerServiceImpl(BannerRepository bannerRepository, CommonFileUtils commonFileUtils) {
this.bannerRepository = bannerRepository;
this.commonFileUtils = commonFileUtils;
}
...
참고 : [Spring]필드 주입(Field Injection) 대신 생성자 주입(Constructor Injection)을 사용해야 하는 이유
컨트롤러에서 왜 @RequiredArgsConstructor 사용하는지 알게 되었습니다. 감사합니다 ^^