본문 바로가기

Spring

[Spring] IOC-DI

#3 IOC와 DI

IOC : 제어의 역전으로써 프로그램의 흐름을 프레임워크가 제어한다는 것입니다

DI : 의존성주입으로 외부에의해 즉 ioc 컨테이너에 의해 의존성이 주입되어 객체를 생성하지않아도 프레임워크가 관리해줍니다.

 

여기서 알다싶이 IOC 라는 개념은 DI 라는 개념을 통해 객체의 생성, 소멸과같은 라이프 사이클을 프레임워크가 관리하는것을 의미할 수 있습니다. 또한 DI 는 IOC 라는 개념을 설명하기위한 디자인패턴이라고 설명할수 있을것같습니다.

Repository나 Service, Controller등 객체를 만들지 않고 사용할 수 있었던 이유는 Spring Boot가 제공하는 IOC Container 덕분입니다.
IOC Container에 등록된 필요객체는 @Autowired를 통해 객체를 만들지 않고, 가져올 수 있고, IOC Container는 @Component를 통해 등록할 수 있습니다.
다음은 대표적인 의존성주입의 예시입니다.

 

    private ArticleService articleService;

    @Autowired // DI, 생성 객체를 가져와 연결
    public ArticleApiController(ArticleService articleService) {
        this.articleService = articleService;
    }

DI

의존성을 주입하는 방법에는 3가지 방법이 존재합니다.

Setter, Field, Constructor 주입이 존재하는데 Spring에선 Constructor 주입을 선호합니다. 이유가 무엇일까요?

순환참조 방지

결과를 먼저 말씀드리면 Constructor는 순환참조를 어플리케이션 구동 시점에 빠르게 파악할 수 있지만 SetterField 주입은 런타임 시점에서야 알 수 있습니다.

이유는 의존성을 주입하는 시점이 다르기 때문인데요. Constructor의 경우 Bean 객체를 생성하는 동시에 의존성을 주입합니다. 메소드에서 원하는 값을 찾아 파라미터로 넘겨줘야 하는것이 이유입니다.

SetterField 주입은 Bean 객체를 먼저 생성한 뒤에 나중에 의존성 주입을 하기 때문에 런타임이 되서야 순환참조라는것을 알 수 있는 것입니다.

Final 키워드

Constructor 주입방식은 final 키워드를 사용할 수 있기에 불변성을 가질 수 있습니다.

또한 객체 생성 시점에 필수적으로 Bean 객체를 초기화 해야하므로 null이 아님을 보장해줍니다.

@RequiredArgsConstructor를 통해 간단하게 작성할 수도 있습니다.

테스트 코드

아래의 코드들은 Field 주입과 Constructor 주입을 비교한것입니다.

@Service
public class MyService {

    @Autowired
    private MyRepository myRepository;

    public void save(int seq){
        myRepository.save(seq);
    }
}

public class MyTest {

    @Test
    public void test(){
        MyService myService = new MyService();
        myService.save(1);
    }
}

해당 코드는 MyServiceMyRepository가 null일 것이고 따라서 save 메소드를 실행하면 NullPointException이 발생합니다. 이는 Test 코드가 Spring과 같은 DI 프레임워크 위에서 동작하지 않기 때문입니다.

 

@Service
public class MyService {
    private final MyRepository myRepository;

    public MyService(MyRepository myRepository){
        this.myRepository = myRepository;
    }

    public void save(int seq){
        myRepository.save(seq);
    }
}

public class MyTest {

    @Test
    public void test(){
        MyRepository myRepository = new MyRepository();
        MyService myService = new MyService(myRepository);
        myService.save(1);
    }
}

해당 코드로 위와같은 상황에 보완이 가능합니다.

 

'Spring' 카테고리의 다른 글

[Spring] Bean  (0) 2023.02.17
[Spring] Spring MVC  (0) 2023.02.16
[Spring] @Transactional  (0) 2023.02.16
[Spring] Redis  (0) 2023.02.11
[Spring] WS/WAS  (2) 2023.01.15