DI, IoC, Bean

DI (의존성 주입)


강한 결합의 문제


예를들어 Controller에서 Service의 객체를 생성하여 사용하고 Service에서 Repository의 객체를 생성하여 사용하고 Repository에서 객체를 선언한다면, Repository 생성자가 변경되었을 때 모든 Controller와 Service의 코드 변경이 필요한데 이 문제를 해결하기 위해서

강한 결합 해결방법

  1. 각 객체에 대한 객체 생성은 딱 1번만
  2. 생성된 객체를 모든 곳에서 재사용

해야 한다.

예를들면,

  1. Repository에서 클래스 선언 및 객체 생성을 하고
    public class Repository{...}
    //객체 생성
    Repository repository = new Repository();
    
  2. Service에서 클래스 선언 및 객체 생성(repository 사용)
    Class Sevice{
     private final Repository repository;
     //repository 객체 사용
     public Service(Repository repository){
         this.repository = repository;//new Repository(); 아니고
     }
    }
    //객체 생성
    Service sevice = new Service(repository);
    
  3. Controller 선언(service 사용)
    Class Controller{
     private final Service service;
     //service 객체 사용
     public Controller(Service service){
         this.service = service;//new Service(); 아니고
     }
    }
    

이렇게 하면 만약 Repository에서 생성자가 변경되어도 Service와 Controller가 변경될 필요가 없다.
결론적으로 강한 결합에서 느슨한 결합으로 바뀐 것이다.




IoC (Inversion of Control)


제어의 역전이란 뜻으로 프로그램의 제어 흐름이 뒤바뀐 걸 의미한다.
일반적으로는 사용자가 자신이 필요한 객체를 생성해서 사용했다면 DI(의존성 주입)으로 인해 IoC 즉, 제어의 역전이 일어나면서(예를 들어, 전에는 Repository를 만들고 Service -> Controller를 생성했다면 IoC에 의해 Controller -> Service -> Repository 순이 되었다는 것?)




Bean


앞선 DI를 사용하기 위해서는 객체 생서잉 우선되어야 했다. 그리고 그 필요한 객체를 생성하여 관리하는 역할을 대신 해주는 게 바로 스프링 프레임워크다.
그리고 Bean은 스프링이 관리하는 객체고, 스프링 IoC 컨테이너는 빈을 모아둔 통이라고 생각하면 된다.



스프링 빈 등록 방법


  1. @Component 클래스 선언 위에 설정하고
@Component
public class TestService{...}

스프링 서버가 뜰 때 스프링 IoC에 빈 저장

//@Component 클래스에 대해 스프링이 해주는 일

//1. Service 객체 생성
TestService testService = new TestService();
//2. 스프링 IoC 컨테이너에 빈(testService) 저장
// testService -> 스프링 IoC 컨테이너

스프링 빈 이름: 클래스 앞글자만 소문자로 변경 public class TestService -> testService

빈 아이콘 확인 img

@Component 적용 조건

  • @ComponentScan에 설정해준 packages 위치와 하위 packages들
  • @SpringBootAplication에 의해 default로 설정되어 있음
  1. @Bean 직접 객체를 생성하여 빈으로 등록 요청
@Configuration
public class BeanConfiguration{
    @Bean
    public TestRepository testRepository(){...}
}

스프링 서버가 뜰 때 스프링 IoC에 빈 저장

//1. @Bean 설정된 함수 호출
TestRepository testRepository = beanConfiguration.testRepository();
//2. 스프링 IoC 컨테이너에 빈(testRepository) 저장
//testRepository -> 스프링 IoC 컨테이너

스프링 빈 이름: @Bean이 설정된 함수명 public TestRepository testRepository(){…} -> testRepository

빈 아이콘 확인 -> 스프링 IoC에 빈이 등록될 것이라는 표시



스프링 빈 사용방법


  1. @Autowired 멤버변수 선언 위에 @Autowired를 하면 스프링에 의해 DI됨
@Component
public class TestService{
    @Autowired
    private TestRepository testRepository;
}

빈을 사용할 함수 위에 @Autowired를 하면 스프링에 의해 DI됨

@Component
public class TestService{
    private final TestRepository testRepository;
    @Autowired
    public TestService(TestRepository testRepository){
        this.testRepository = testRepository;
    }

}

@Autowired 적용 조건
스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능

@Autowired 생략 조건

  • Spring 4.3버전부터 생략 가능
  • 생성자 선언이 1개일 때만 생략 가능
  • Lombok의 @RequiredArgsConstructor를 사용하면
@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class TestController {

    private final TestService testService;
    
    // 생략 가능
		// @Autowired
		// public TestController(TestService testService) {
		//     this.testService = testService;
		// }
}
  1. ApplicationContext
    스프링 IoC 컨테이너에서 빈을 수동으로 가져오는 방법
@Component
public class TestService {
    private final TestRepository testRepository;

    @Autowired
    public TestService(ApplicationContext context) {
        // 1.'빈' 이름으로 가져오기
        TestRepository testRepository = (TestRepository) context.getBean("testRepository");
        // 2.'빈' 클래스 형식으로 가져오기
        // TestRepository testRepository = context.getBean(TestRepository.class);
        this.testRepository = testRepository;
    }	
}

출처: 스파르타코딩클럽 Spring 심화반