DI, IoC, Bean
10 Jul 2022DI (의존성 주입)
강한 결합의 문제
예를들어 Controller에서 Service의 객체를 생성하여 사용하고 Service에서 Repository의 객체를 생성하여 사용하고 Repository에서 객체를 선언한다면, Repository 생성자가 변경되었을 때 모든 Controller와 Service의 코드 변경이 필요한데 이 문제를 해결하기 위해서
강한 결합 해결방법
- 각 객체에 대한 객체 생성은 딱 1번만
- 생성된 객체를 모든 곳에서 재사용
해야 한다.
예를들면,
- Repository에서 클래스 선언 및 객체 생성을 하고
public class Repository{...} //객체 생성 Repository repository = new Repository();
- Service에서 클래스 선언 및 객체 생성(repository 사용)
Class Sevice{ private final Repository repository; //repository 객체 사용 public Service(Repository repository){ this.repository = repository;//new Repository(); 아니고 } } //객체 생성 Service sevice = new Service(repository);
- 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 순이 되었다는 것?)
- 용도에 맞게 필요한 객체를 가져다 사용
- 사용할 객체가 어떻게 만들어졌는지 알 필요 없음
- 실생활 예제) 가위의 용도별 사용
- 음식을 자를 때 필요한 부엌가위(생성되어 있는 객체 kitchenScissors)
- 무늬를 내며 자를 때 필요한 핑킹가위(생성되어 있는 객체 pinkingShears)
- 정원 나무를 다듬을 때 필요한 전지가위(생성되어 있는 객체 pruningShears)
Bean
앞선 DI를 사용하기 위해서는 객체 생서잉 우선되어야 했다. 그리고 그 필요한 객체를 생성하여 관리하는 역할을 대신 해주는 게 바로 스프링 프레임워크다.
그리고 Bean은 스프링이 관리하는 객체고, 스프링 IoC 컨테이너는 빈을 모아둔 통이라고 생각하면 된다.
스프링 빈 등록 방법
- @Component 클래스 선언 위에 설정하고
@Component
public class TestService{...}
스프링 서버가 뜰 때 스프링 IoC에 빈 저장
//@Component 클래스에 대해 스프링이 해주는 일
//1. Service 객체 생성
TestService testService = new TestService();
//2. 스프링 IoC 컨테이너에 빈(testService) 저장
// testService -> 스프링 IoC 컨테이너
스프링 빈 이름: 클래스 앞글자만 소문자로 변경 public class TestService -> testService
빈 아이콘 확인
@Component 적용 조건
- @ComponentScan에 설정해준 packages 위치와 하위 packages들
- @SpringBootAplication에 의해 default로 설정되어 있음
- @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에 빈이 등록될 것이라는 표시
스프링 빈 사용방법
- @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;
// }
}
- 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;
}
}