IoC 컨테이너 - ApplicationContext
컨테이너가 관리하는 객체를 빈(bean)이라고 하고, 이 빈(bean)들을 관리한다는 의미로 컨테이너를
빈 팩토리(BeanFactory) 라고 부른다.
BeanFactory에 여러 가지 컨테이너 기능을 추가하여 애플리케이션 컨텍스(ApplicationContext)라고 부름
ApplcationContext : 얘가 Bean들을 만들고, 그 Bean들의 의존성을 엮어준다. 오로지 Bean들만 관리!
OwnerController 가 IoC 컨테이너 내부에 들어오고, 컨테이너에서 OwnerController 객체를 만들어 준다.
그리고, OwnerRepository 의 객체도 만들어 준다. -> Bean
즉, Bean들의 의존성들을 자동으로 관리해준다.
IoC 컨테이너는 Bean들의 의존성을 엮어주며 주입을 자동적으로 해주는 역할을 한다.
IoC 컨테이너 : OwnerController가 Bean으로 등록이 되고 (@Controller라는 어노테이션이 붙어있기 때문에), IoC 컨테이너 내부에 들어가서 그 객체들을 만들고, 그 객체들의 의존성을 엮어준다. OwnerRepository 라는 Bean을 찾아서 생성자에다가 의존성을 주입시켜주는 일을 한다.
Bean을 등록하는 방법
1. @Component Scanning
Annotation (어노테이션)
- @Component
@Repository
@Service
@Autowired (의존성 주입)
class 상단에 @Component , @Repository , @Service , @Controller 어노테이션이 붙으면 자동으로
IoC 컨테이너에 Bean으로 등록이 된다.
2. @Bean을 직접 등록하는 방법. ( 잘 안씀)
빈을 직접 일일히 등록할때에는 @Configuration 이라는 어노테이션이 등록된 클래스 안에 정의해야한다.
@SpringBootApplication 를 타고 들어가보면 @Configuration 어노테이션이 있다.
@Autowired : Bean을 꺼내쓰는 방법
@RestController //#@RestController안에 @Controller가 있고, 그안에 @Component가 있으므로 Bean이다.
public class SampleController {
@Autowired
ApplicationContext applicationContext;
@Autowired
String bean_name; // String타입의 bean_name 이라는 이름의 Bean을 쓰겠다.
//IoC 컨테이너가 bean_name이라는 Bean을 찾아서 알아서 넣어준다. (컨테이너의 역할!)
@GetMapping("/context")
public String context(){
return "hello " + bean_name; //쓰면 된다. (원래 있는것처럼)
}
}
@Autowired 를 사용해서 ApplicationContext 안에 있는 Bean들을 꺼내쓸 수 있다.
xml 설정파일에 다음 코드를 추가해야한다.
<context:component-scan base-package = "com.application.demo">
com.application.demo 라는 package에 있는 bean을 *스캔하여 등록하겠다 라는 뜻.
*Scanning을 할때에는 어노테이션을 확인하고 bean으로 자동으로 등록해준다.
?? @Autowired라는 어노테이션이 없는데 어떻게 빈으로 등록이 된걸까?
어떤 빈이 되는 클래스의
생성자가 오직 하나만 있고 그 생성자의 매개변수 타입이 빈으로 등록이 되어있다면
@Autowired가 없더라도 자동으로 주입을 시켜준다.
Repository라는 interface 를 구현한 interface는 Spring Data JPA에서 .. LifeCycle .. 어쩌고 에서 bean으로 자동으로 등록해준다.... 그래서 따로 @Autowired라는 어노테이션이 없어도 bean으로 등록이 된다.
이렇게 되면 @Autowired 라는 어노테이션이 점점 사라진다고 한다. 이런식으로 코드를 짜면.
@Autowired / @Inject를 어디에 붙일까?
- 생성자 ( 어떠한 클래스에 반드시 필요한 의존성이다 하면 생성자)
빈이 없으면 인스턴스를 생성을 하지 못한다. ->
- Setter (만약 *어느 의존성에 대한 Setter를 가지고 있다면 Setter)
*어느 의존성 -> private final PetRepository pets;
- 필드 (Setter가 없다면 .. 하지만 @Autowired를 위해서 굳이 Setter를 만들어서까지 할 필요는 없다.)
내가 어떤 빈을 주입받았는지 확인 하고 싶을때
1. Runner를 하나 만들어 준다. (implements ApplicationRunner)
2. 빈을 주입 받으면, 주입 받은 빈의 클래스이름을 출력하게끔 해준다.
3. @Primary - 같은 타입의 빈이 여러개 일때
같은 타입의 빈이 여러개여도 @Primary로 등록해주면 그 빈이 주입이 된다.
아래는 실행했을때의 출력문 이다.
같은 타입의 빈이 여러개일때, 명확하게 표시를 안해주면 오류가 난다.
그리하여 이 오류에 대한 해결책은 다음과 같이 3가지가 있다.
- @Primary ("이 빈을 받을거다" 라고 마킹하는 용도)
- 해당 타입의 빈 모두 주입 받기
- @Qualify (빈 이름으로 주입)
@Primary
이 빈을 주입 하도록 표시하는 용도.
@Qualifier (빈 이름으로 주입) *이 방법보단 @Primary가 더 안전하다.
필드에다가 어노테이션을 붙여준다.
@Autowired @Qualifier("빈의 이름")
해당 타입의 빈 모두 주입 받기
모든 타입의 빈을 주입 받으려면 위에 코드를 추가해주면 된다.
그러면 해당 타입의 모든 빈이 출력이 된다.
- AutowiredAnnotationBeanPostProcessor 가 빈으로 등록이 되어있는지 확인하는 코드