[Spring] 필드 주입과 생성자 주입

시나브로·2021년 5월 31일
0

Spring

목록 보기
1/3
post-thumbnail

0. 들어가기 전에


  의존성 주입할 때 왜 @Autowired를 지양해야하는걸까. intelliJ에서는 다음과 같이 말하고 있다

Field injection is not recommended
Inspection info: Spring Team recommends: "Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies".

Always 생성자 주입을 권장하고 있다. 각각의 장단점에 대해 정리하지 전에 먼저 의존성 주입 3가지 방법에 대해 알아보고 들어가자



1. 의존성 주입 3가지 방법




1.1 생성자 주입(Constructor Injection)


CodeInputFileService.class


@Service
public class CodeInputFileService {

    private final CodeInputFileManager codeInputFileManager;

    public CodeInputFileService(CodeInputFileManager codeInputFileManager) {
        this.codeInputFileManager = codeInputFileManager;
    }

...

  단일 생성자인 경우에는 @Autowired 어노테이션 조차 붙이지 않아도 되지만 생성자가 2개 이상인 경우에는 생성자에 어노테이션을 붙여주어야 한다.


1.2 필드 주입(Field Injection)


CodeInputFileService.class



@Service
public class CodeInputFileService {

    @Autowired
    private CodeInputFileManager codeInputFileManager;

    ...

  @Autowired 선언으로 필드에서 주입이 가능하다. 간편한 방법으로 많이 사용하지만 이번에는 왜 이를 지양해야하는지 알아본다


1.3 수정자 주입(Setter Injection)


CodeInputFileService.class



@Service
public class CodeInputFileService {

    private CodeInputFileManager codeInputFileManager;

    @Autowired
    public void setCodeInputFileManager(CodeInputFileManager codeInputFileManager) {
        this.codeInputFileManager = codeInputFileManager;
    }

    ...

  Setter 주입은 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다. 주로 사용하지는 않는 방법이다.




2. 생성자 주입을 권장하는 이유



2.1 순환참조 방지


CodeInputFileService.class


@Service
public class codeInputFileService {

    @Autowired
    private BoardService boardService;


    ...

CodeInputFileService.class


@Service
public class BoardService {

    @Autowired
    private CodeInputFileService  codeInputFileService;


    ...

  위와 같이 서로 순환 참조하는 구조가 되었을 때, 필드 주입으로 구현했다면, 해당 참조가 실행돼 StackOverflowError가 발생하기 전까지는 알 수 없다. 하지만 생성자 주입으로 구현되어있다면 어플리케이션이 구동될 때 다음과 같이 에러가 나오는 것을 볼 수 있다.


***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

 mainController (field private com.wcp.board.BoardService com.wcp.board.main.MainController.boardService)
┌─────┐
|  boardService defined in file [C:\git\2021\WebCompile\Toy-Project\core\out\production\classes\com\wcp\board\BoardService.class]
↑     ↓
|  codeInputFileService defined in file [C:\git\2021\WebCompile\Toy-Project\core\out\production\classes\com\wcp\coding\inputFile\CodeInputFileService.class]
└─────┘


  생성자 주입을 사용하지 않으면, 해당 코드를 호출하기 전까지는 순환참조를 알수가 없다. 따라서 생성자 주입을 통해 런타임시 해당 에러를 잡을 수 있다.




2.2 불변성(Immutability)


  필드 주입과 수정자 주입은 해당 필드를 final로 선언할 수 없다. 수정자 주입이나 일반 메소드 주입을 이용하게 된다면 불필요하게 수정의 가능성을 열어두게 되는 것으로 차후 변경이 되었을 때 발생하는 Side-Effect를 사전에 감지할 수 없다



2.3 테스트 코드 작성


  DI의 핵심은 관리되는 클래스가 DI 컨테이너에 의존성이 없어야 한다는 것이다. 즉, 독립적으로 인스턴스화가 가능한 POJO(Plain Old Java Ojbect) 여야 한다는 것이다. DI 컨테이너를 사용하지 않고서도 단위 테스트에서 인스턴스화할 수 있어야 한다. 생성자 주입을 하게 되면, Test Case 생성 시, 원하는 구현체를 넘겨주면 되고, 구현체를 넘겨주지 않은 경우에는 객체생성 자체가 불가능하기 때문에 테스트하기도 편하다.


CodeInputFileServiceTest.class

@TestConstructor(autowireMode = TestConstructor.AutowireMode.ALL)
public class CodeInputFileServiceTest {

    private final CodeInputFileService codeInputFileService;

    public CodeInputFileServiceTest(CodeInputFileService codeInputFileService) {
        this.codeInputFileService = codeInputFileService;
    }

    @Test
    public void methodTest(){

    ...



3. 마치며


  편리함으로 사용해왔던 필드 주입은 지양하고 생성자 주입을 지향하는 것을 권한다.








참고

profile
Be More!

0개의 댓글