Dependency Injection

우리가 지금까지 써왔던 Field Injection Type. 왜 바꿔야 할까?

https://www.petrikainulainen.net/software-development/design/why-i-changed-my-mind-about-field-injection/

스프링에서의 소개

https://spring.io/blog/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required

– 번역

http://opennote46.tistory.com/216

Injection 방법 세가지 타입을 예제를 들어 Bad, Ugly, Good으로 평가

https://kinbiko.com/java/dependency-injection-patterns/

http://olivergierke.de/2013/11/why-field-injection-is-evil/

Field Injection 변경하는 것을 고려해야 하는 좀 더 자세한 내용

https://www.vojtechruzicka.com/field-dependency-injection-considered-harmful/

– 번역

http://www.mimul.com/pebble/default/2018/03/30/1522386129211.html

Injection 방법

Constructor

private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;

@Autowired
public DI(DependencyA dependencyA, DependencyB dependencyB, DependencyC dependencyC) {
   this.dependencyA = dependencyA;
   this.dependencyB = dependencyB;
   this.dependencyC = dependencyC;
}

Setter

private DependencyA dependencyA;
private DependencyB dependencyB;
private DependencyC dependencyC;

@Autowired
public void setDependencyA(DependencyA dependencyA) {
   this.dependencyA = dependencyA;
}

@Autowired
public void setDependencyB(DependencyB dependencyB) {
   this.dependencyB = dependencyB;
}

@Autowired
public void setDependencyC(DependencyC dependencyC) {
   this.dependencyC = dependencyC;
}

Field

@Autowired
private DependencyA dependencyA;

@Autowired
private DependencyB dependencyB;

@Autowired
private DependencyC dependencyC;

왜 Constructor Injection을 권장하나?

  1. 단일 책임의 원칙
    생성자의 인자가 많을 경우 코드량도 많아지고, 의존관계도 많아져 단일 책임의 원칙에 위배된다.
    그래서 Constructor Injection을 사용함으로써 의존관계, 복잡성을 쉽게 알 수 있어 리팩토링의 단초를 제공하게 된다.
  2. 테스트 용이성
    DI 컨테이너에서 관리되는 클래스는 특정 DI 컨테이너에 의존하지 않고 POJO여야 한다.
    DI 컨테이너를 사용하지 않고도 인스터화 할 수 있고, 단위 테스트도 가능하며 다른 DI 프레임워크로 전환할 수도 있게 된다.
  3. Immutability
    Constructor Injection에서는 필드는 final로 선언할 수 있다.
    불변 객체가 가능한데 비해 Field Injection은 final을 선언할 수 없기 때문에 객체가 변경 가능한 상태가 된다.
  4. 순환 의존성
    Constructor Injection에서는 멤버 객체가 순환 의존성을 가질 경우 BeanCurrentlyInCreationException이 발생해서 순환 의존성을 알 수 있게 된다.
  5. 의존성 명시
    의존 객체 중 필수는 Constructor Injection을 옵션인 경우 Setter Injection을 활용할 수 있다.

Lombok을 활용한 Constructor Injection 개발 편의성은 좋아질 수 있으나, 의존관계의 복잡성을 명확하게 보여주진 못하게 된다.

@RequiredArgsConstructor는 초기화 되지 않은 final 필드를 매개 변수로 취하는 생성자를 생성하고,
@NonNull이 필드는 null체크가 실행되고 파라미터가 null인 경우 NullPointerException을 발생시킨다.

Spring 4.3 이상

@RequiredArgsConstructor
@Component
public class ConstructorInjection {
   @NonNull
   private final LoginService loginService;
   @NonNull
   private final SignupService signupService;
}

Spring 4.3 이전

@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Component
public class ConstructorInjection {
   @NonNull
   private final LoginService loginService;
   @NonNull
   private final SignupService signupService;
}

결론

  • Constructor Injection과 Setter Injection을 적절히 사용하자.
  • 클래스 하나에 너무 많은 책임을 위임하지 말자.
  • Constructor Injection에 적절한 DI는 3개.
    5개 이상 넘어간다면 클래스를 분리하여 역할을 분산한다.

안녕하세요. 끄적이기를 좋아하는 개발자 이예빈입니다. 매일 일기를 쓰는 것 처럼 블로그를 쓰고 싶어요.
Leave a Reply

Your email address will not be published. Required fields are marked *