좋은 코드는 응집성은 높게, 결합성은 낮게를 추구한다. IoC를 이용하면 결합성을 낮출 수 있으므로 DI를 이용하게 되는데, 사실 Controller -> Service -> Repository로 받아들인 나는 아직 이해할 수 가 없었다.
강한 결합인 상태에선 Controller에서 Service객체를 만들어서 이용했는데, 역전되면 누가 Service를 만들고 더 나아가 Repository객체는 누가 만드는가 하는 의문이 생길 수 밖에 없다.
위에서 의문의 답을 바로 말하자면, Spring 프레임워크가 객체를 미리 생성하고 관리해준다. 그렇다면, 미리 생성의 대상은 어떻게 지정되는가 하는 의문이 추가로 발생하게 된다. 이는 @Bean이라는 어노테이션을 통해서 지정이 가능하다. 이 커피콩을 모아둔게 바로 IoC 컨테이너다.
@Component라는 어노테이션이 입력된 클래스는 Bean으로 등록이 된다.
@Component
public class MemoService { ... }
Spring을 Run 하면 서버가 열리고, 이때 IoC 컨테이너에 Bean들이 저장된다.
즉, Spring이 MemoService memoService = new MemoService();이 코드를 실행해서 memoService를 IoC 컨테이너에 저장한다. 이때 Bean의 이름은 클래스 에서 맨 앞 글자를 소문자로 바꾼게 된다. MemoService -> memoService
이렇게 되면 IntelliJ에서 해당 클래스 옆에 원두 모양의 아이콘이 생성된다.
Spring 서버 실행시 @Component에 설정한 packages를 포함해서 하위 packages들을 전부 확인해서 @Componet대상 클래스를 전부 Bean으로 등록하는데, @ComponentScan은 프로젝트 생성시 ~Application.class에 @SpringBootApplication에 내장되어 있다.
@Component
public class Service {
@Autowired
private Repository Repository;
// ...
}
Spring에서 IoC 컨테이너에 저장된 Repository Bean을 해당 필드에 의존성을 주입해준다.
@Component
public class Service {
private final Repository Repository;
@Autowired
public Service(Repository Repository) {
this.Repository = Repository;
}
// ...
}
일반적으론 위처럼 생성자를 사용해서 DI한다.
즉, @Autowired가 있으면 Repository Bean을 주입해준다는 것
Spring IoC 컨테이너에 의해 관리되는 클래스에서만 가능하다.
Spring 4.3부터 @Auto Wired 생략이 가능하다. 다만, 생성자 선언이 1개 일 경우만 가능하다.
오버로딩 된 생성자들이 있으면 다 입력해줘야함
public MemoService(ApplicationContext context) {
// 1.'Bean' 이름으로 가져오기
MemoRepository memoRepository = (MemoRepository) context.getBean("memoRepository");
// 2.'Bean' 클래스 형식으로 가져오기
// MemoRepository memoRepository = context.getBean(MemoRepository.class);
this.memoRepository = memoRepository;
}
BeanFactory은 Bean의 생성, 관계 설정등의 제어를 담당하는 IoC 객체인데, 이를 상속해서 기능을 확장한 Container가 ApplicationContext다.
ApplicationContext를 이용하면 IoC컨테이너에서 Bean을 수동으로 가져올 수 있다.
개발자가 편하게 보라고 @Component를
- @Controller, @RestController
- @Service
- @Repository
로 구분해서 역할을 명시하게 해줬다. 그래서 @Component를 사용해도 되지만, 해당 클래스에는 해당 Layer에 일치하는 Component명을 입력하면 된다.