Goal
- @Autowired에 대해 알아본다
- 해당 타입의 빈이 없거나 하나인 경우, @Autowired의 동작
- 해당 타입의 빈이 여러개인 경우, @Autowired의 동작
- @Autowired의 동작원리를 알아본다
@Autowired 란?
필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입하며, 기본 값은 (required=true) 못 찾으면 어플리케이션 구동이 실패한다.
해당 타입의 빈이 없거나 하나인 경우 @Autowired의 동작
1. 생성자
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
// bean 등록 x
public class BookRepository {
}
결과
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.demosping51.BookService required a bean of type 'com.example.demosping51.BookRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.demosping51.BookRepository' in your configuration.
=> bookService의 생성자에 0번째 파라미터로 세팅한 bookRepository에 해당하는 bean이 없으므로 bean으로 등록하라
// bean 등록 o
@Repository
public class BookRepository {
}
결과
문제 없이 어플리케이션 실행
정리
생성자 의존성 주입은 bean을 만들 때에도 개입이 된다. 해당하는 생성자에 전달받아야 하는 타입에 bean이 없다면 인스턴스를 만들지 못하고, bookService도 등록이 안됨
2. setter
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
public class BookRepository {
}
결과
Description:
Parameter 0 of method setBookRepository in com.example.demosping51.BookService required a bean of type 'com.example.demosping51.BookRepository' that could not be found.
Action:
Consider defining a bean of type 'com.example.demosping51.BookRepository' in your configuration.
@Autowired(required=false)
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
결과
문제 없이 어플리케이션 실행
정리
생성자 주입과 달리 bookService 인스턴스 자체는 만들 수 있다. 그러나 @Autowired에 따라 어플리케이션이 실행여부가 달라진다.
@Autowired가 있을 경우, required=false로 세팅하면 옵션이기 때문에 어플리케이션이 구동된다. 그러나 따로 설정하지 않으면 기본값은 required=true이기 때문에 구동되지 않는다.
3. 필드
@Service
public class BookService {
@Autowired(required = false)
BookRepository bookRepository;
}
결과
문제 없이 어플리케이션 실행
정리
@Autowired를 optional로 설정하여 bookService가 해당하는 의존성 없이도 등록이 가능하다
해당 타입의 빈이 여러개인 경우 @Autowired의 동작
사용할 빈을 명시 x
public interface BookRepository {
}
@Repository
public class RoiseRepository implements BookRepository{
}
@Repository
public class KimRepository implements BookRepository{
}
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
}
결과
***************************
APPLICATION FAILED TO START
***************************
Description:
Field bookRepository in com.example.demosping51.BookService required a single bean, but 2 were found:
- kimRepository: defined in file [/Users/daseulkim/Desktop/portfolio/demosping51/target/classes/com/example/demosping51/KimRepository.class]
- roiseRepository: defined in file [/Users/daseulkim/Desktop/portfolio/demosping51/target/classes/com/example/demosping51/RoiseRepository.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
=> repository에 해당하는 bean이 두개가 발견이 되었는데, 둘중에 어떤 걸 주입해야 할지 모르겠어서 에러가 났으니
1. @Primary 어노테이션을 통해 어느 bean을 사용하고 싶은지 마킹하거나, 2. @Qualifier을 사용하거나, 3. 모든 bean을 다 받아라
1. @Primary
@Repository @Primary
public class RoiseRepository implements BookRepository{
}
@Repository
public class KimRepository implements BookRepository{
}
결과
문제 없이 어플리케이션 실행
// 클래스 확인용 Runner
@Component
public class BookServiceRunner implements ApplicationRunner {
@Autowired
BookService bookService;
@Override
public void run(ApplicationArguments args) throws Exception {
bookService.printBookRepository();
}
}
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
public void printBookRepository(){
System.out.println(bookRepository.getClass());
}
}
결과
class com.example.demospring51.RosieRepository
정리
bookService에서는 bookRepository를 주입 시,@Primary가 있는 bean을 주입한다.
@Qualifier보다 type safe 하다
2. @Qualifier
@Service
public class BookService {
@Autowired @Qualifier("roiseRepository")
BookRepository bookRepository;
public void printBookRepository(){
System.out.println(bookRepository.getClass());
}
}
결과
class com.example.demospring51.RosieRepository
정리
@Qualifier에 빈의 id (= camel 기법의 클래스 이름)를 적어 해당 bean을 주입한다.
@Primary보다 type safe 하지 않다.
3. 모든 bean을 받기
@Service
public class BookService {
@Autowired
List<BookRepository> bookRepositories;
public void printBookRepository(){
this.bookRepositories.forEach(System.out::println);
}
}
결과
com.example.demosping51.KimRepository@a7ad6e5
com.example.demosping51.RoiseRepository@3b1ed14b
@Autowired의 동작 원리
라이프 사이클
BeanPostProcessor 라이프 사이클 인터페이스 구현체에 의해서 동작한다
BeanPostProcessor
1. bean initialization 빈 생성
2. bean 인스턴스 생성
3. bean initialization 라이프사이클 이전 / 이후 부가적인 작업을 하는 또 다른 라이프사이클 콜백 (ex. @PostConstruct, InitializingBean 인터페이스)
그 중에서도 AutowiredAnnotationBeanPostProcessor가 처리를 해주는 것!
참고
스프링입문 강의 by 백기선
https://www.inflearn.com/course/lecture?courseSlug=spring&unitId=15538 https://www.inflearn.com/course/spring_revised_edition/dashboard
https://www.inflearn.com/course/spring_revised_edition/dashboard
'Spring > about spring' 카테고리의 다른 글
[IoC 컨테이너와 빈] 빈의 스코프 (0) | 2023.07.22 |
---|---|
[IoC 컨테이너와 빈] @Component Scan (0) | 2023.07.22 |
[IoC 컨테이너와 빈] ApplicationContext (0) | 2023.07.19 |
[스프링 삼각형] PSA 잘 만든 인터페이스 (0) | 2023.07.17 |
[스프링 삼각형] AOP 관점 지향 프로그래밍 (0) | 2023.07.11 |