본문 바로가기

JAVA/- Spring

[spring] DI 알아보기

DI(Dependency Injection)이란? 

Inversion of Control 이라고도 하는 의존 관계 주입(Dependency Injection)이라고도 하며, 어떤 객체가 사용하는 의존 객체를 직접 만들어 사용하는게 아니라, 주입 받아 사용하는 방법입니다. (new 연산자를 이용해서 객체를 생성하는 것을 받아 쓰는 것입니다.)

 

setter와 생성자로 객체를 받아서 쓸 수 있습니다.

 

import org.springframework.stereotype.Service;

@Service
public class BookService {

    private BookRepository bookRepository;

    public BookService(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }
}
@Service
public class BookRepository {
      // DI Test
}

BookService 클래스가 만들어지기 위해서는 bookRepository가 필요합니다. 

그럼 BookService가 만들어 질 때 동작하는 생성자를 살펴보면 매개변수로 받습니다. 이것을 의존성 주입이라고 합니다. BookService가 BookRepository를 받아야지 생성이 되니 의존하다고 표현합니다.

이와 같이 생성자로 사용하여 주입받는 것을 IOC(Inversion of Control)이라고 합니다.

 

import org.junit.Test;

public class BookServiceTest {

    @Test
    public void save() {
        BookRepository bookRepository = new BookRepository();
        BookService bookService = new BookService(bookRepository);
    }
}

이렇게 다른 곳에서 객체를 new 생성하고(BookRepository)

BookService를 new로 생성할 때 미리 만들어둔 bookRepository 개체를 넣어주면 됩니다.

 

 

이런식으로 사용자가 하나하나 넣어줄 수 있지만 스프링을 사용하면 더 편리하게 의존성을 넣어 줄 수 있습니다.

BookService와 BookRepository 둘다 Bean으로 등록되어 있을 때 BookService의 생성자만 만들어 주면 스프링 Ioc 컨테이너가 BookRepository에 의존성 주입을 알아서 해줍니다.(생성자 위에 @Autowired를 붙히면 됩니다.)

 

평범하게 의존성 주입해보기(예시)

public class Store {

    private Apple apple

    public Store() {
        this.apple = new Apple();
    }

}

하나의 상점에 사과 1개를 판매하는 Store 클래스가 있습니다.

Store 클래스는 Apple 클래스와 강하게 결합되어 있는 문제가 있습니다.

 

만약 바나나를 판다고 하면 Apple 객체를 모두다 Banana라고 바꿔야 할 것입니다.

즉, 생성자의 변형이 필요합니다.

public class Store {

    private Banana banana

    public Store() {
        this.banana = new Banana();
    }

}

 

그리고 객체들 간의 관계가 아니라 클래스 간의 관계까 맺어졌다고 합니다

Store와 Apple 아니라 Store와 fruit 간의 관계를 맺어야 합니다. 왜냐하면 Store에서는 Apple 뿐만 아니라 다른 과일들도 팔 예정이기 때문입니다. 

public interface Fruit {

}

public class Apple implements Fruit {

}

이런식으로 Fruit 인터페이스를 만듭니다. 그리고 Apple은 이런 Fruit 인터페이스를 상속받습니다.

Fruit의 구현체 안에 Apple이 들어가있습니다.

 

 

이제 외부에서 Fruit를 주입 합니다. 

public class Store {

    private Fruit fruit;

    public Store(Fruit fruit) {
        this.fruit = fruit;
    }

}

이러면 Store에서 Fruit 안에 어떤 클래스가 오든 구현체 클래스에 의존하지 않게 됩니다.

 

 

스프링으로 의존성 주입해보기(예시)

public class BeanFactory {

    public void store() {
        // Bean의 생성
        Fruit apple = new Apple();
    
        // 의존성 주입
        Store store = new Store(apple);
    }
    
}

이런식으로 Bean을 생성한다음 new로 인스턴스를 생성할 때 구현체를 넣어줍니다. 이 때 BeanFactory에서 진행이 됩니다.

이 BeanFactory는 스프링이 관리를 합니다. 

 

@Autowired로 주입받기

public class Store {

    @Autowired
    private Fruit fruit;

}

@Autowired를 사용하려면 Bean으로 등록된 객체만 의존 주입이 가능합니다. 

spring이 ApplicationContext가 Bean을 관리합니다. Fruit가 Bean에 등록이 되어있고 객체를 생성하려면(주입받으려면) @Autowired가 필요합니다. 

 

스프링 4.3부터는 생성자가 1개일 경우 @Autowired 생략이 가능합니다.

@RequiredArgumentConstructor로 주입받기

@RequiredArgumentConstructor
public class Store {

    private final Fruit fruit;

}

@RequiredArgumentConstructor 어노테이션을 사용하면 final을 붙을 객체를 자동으로 주입시켜줍니다. 이 때도 주입시킬 객체가 Bean으로 등록이 되어 있어야 합니다.

 

 

생성자로 주입 받는 것이 가장 좋다고 합니다. 참고 : https://mangkyu.tistory.com/125

 

참고

https://steady-hello.tistory.com/120

https://galid1.tistory.com/493?category=769011

https://mangkyu.tistory.com/125

 

728x90