Dependency Injection

의존성 주입의 방법은 총 3가지가 있다.

Constructor Injection

  • Spring 4.3에서부터는 단일 생성자의 경우 Autowired Annotation 불필요함
@Service
public class Atin {
  private final Story story;

  @Autowired
  public Atin(Story story) {
    this.story = story;
  }
}

Field Injection

...

@Service
public class Atin {
  @Autowired
  private final Story story;
}

Setter Injection

...

@Service
public class Atin {
  private Story story;

  @Autowired
  public void setStory(Story story) {
    this.story = story;
  }
}

어떤 방법이 가장 좋을까?

여러 가지 방법이 있고, 어떤 형태로 하던 개발에는 문제는 없다.
그러나 프로젝트 개발, 그리고 회사의 팀에서 개발을 할 때는 공통된 코드 스타일 작성이 필요하다.

가장 추천하는 방법은 생성자 주입이다.
스프링 소스를 살펴보면 생성자 주입을 이용한 방법을 사용하고 있다.

생성자 주입이 좋은 내가 생각하는 이유는 다음과 같다.

  • 테스트 클래스 작성시 리플렉션을 이용하지 않고 주입할 수 있다.
  • POJO에 가깝다.
  • 특별한 경우가 아니라면, 의존관계에 있는 객체들은 반드시 의존관계 주입이 되어야 하며 그렇다면 생성자 주입이 그림상 맞다.

[1]의 블로그에 보면 해당 이유를 다음과 같이 설명하고 있다.

  • 단일 책임의 원칙
  • 테스트 용이성
  • Immutabiblity
  • 순환 의존성
  • 의존성 명시

그렇다면 생성자 주입이 가장 좋은 방법으로 생각해 볼 수 있다.
그런데 생성자를 만드는 방법이 꽤 불편하기도 한데 이럴 때 고려를 해 볼 수 있는 점이 lombok이다.

주입이 필요한 객체들을 final로 한 이후에 lombok의 @RequiredArgsConstructor를 사용하면 final로 지정된 객체들이 자동적으로 생성자 때 필요하게 되면서 편리하게 생성자 주입을 사용할 수 있다.

@Service
@RequiredArgsConstructor
public class TestController {
    private final StoryService storyService;
    private final TestService testService;

    ...
}

그러면 lombok을 통한 방법의 생성자 주입이 최선일까?
처음에는 이 방식이 편리해서 사용했고 편리했다.
그러나 이 방식에는 몇 가지 문제가 존재한다.

  • 주입하려는 bean이 1개가 아닌 경우에는 @Qualifier를 사용해야 하는데 lombok 이용시 불가능하다.
  • @Value와 같은 properties 등의 주입이 필요할 경우 불가능하다. 그렇다고 @Value경우는 필드 주입을 쓰게 되면 코드 통일성이 없어진다.
  • final로 지정된 클래스 변수들의 순서가 변경이 되면 생성자도 변경이 되며 TC등에서 문제가 발생할 수 있다.
  • 생성자에 특정 로직 작성이 필요한 등의 이슈가 발생하면 결국 직접 생성을 해줘야 한다.

그래서 결론은, 생성자 주입은 lombok을 사용하지 않고 직접 작성하는 것이 좋다.
다음은 spring sagan 소스이다. 참고용으로 넣어본다.
Spring 4.3 이후 버전이라서 Autowired도 필요가 없다.

@RestController
@RequestMapping(path = "/guides", produces = MediaTypes.HAL_JSON_VALUE)
public class GuidesController {

    private final GuideRenderer guideRenderer;

    private final GithubClient githubClient;

    private final RendererProperties properties;

    private final GuideResourceAssembler guideAssembler = new GuideResourceAssembler();

    public GuidesController(GuideRenderer guideRenderer, GithubClient github,
            RendererProperties properties) {
        this.guideRenderer = guideRenderer;
        this.githubClient = github;
        this.properties = properties;
    }

    ...
}

https://github.com/spring-io/sagan/blob/master/sagan-renderer/src/main/java/sagan/renderer/guides/GuidesController.java

Reference

[1] https://landsnail.tistory.com/entry/Spring-%EC%9D%98%EC%A1%B4%EC%84%B1-%EC%A3%BC%EC%9E%85-%EB%B0%A9%EB%B2%95
[2] https://github.com/sieunkr/spring-tips/tree/master/spring-di
[3] https://javaslave.tistory.com/19

Leave a Comment


to Top