Spring IoC Container와 Autowired
Autowired란 ?
필요한 의존 객체의 타입(Type)에 해당하는 빈을 찾아 주입한다.
@Autowired
- required: 기본값은 true (따라서 못 찾으면 애플리케이션 구동에 실패한다.)
사용할 수 있는 위치
1. Constructor
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public BookService(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
만약 BookRepository가 @Component 혹은 @Repository로 선언되지 않았다면
- Parameter 0 of constructor in com.springstudy.demoppring51.BookService required a bean of type ‘com.springstudy.demoppring51.BookRepository’ that could not be found. 라는 문구가 뜬다.
0번째 타입에 맞는 파라미터 (BookRepository bookRepository)를 찾을 수 없다는 뜻.
2. Setter
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
3. Field
@Service
public class BookService {
BookRepository bookRepository;
@Autowired
public void setBookRepository(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
}
Bean 등록시…
해당 타입의 Bean이 없는 경우
- 오류 -> required = false 를 통해 오류가 나지 않도록 할 수 있다.
해당 타입의 Bean이 한 개인 경우
- 정상 등록
해당 타입의 Bean이 여러 개인 경우
- Bean 이름으로 시도,
- 같은 이름의 Bean 찾으면 해당 Bean 등록
같은 타입의 Bean이 여러 개인 경우 해결 방법
다음과 같이 BookRepository는 interface이며 이를 상속받은 구현체가 ABookRepository, BBookRepository 2개가 존재한다면
BookRepository.java
public interface BookRepository {
}
ABookRepository.java
@Repository
public class ABookRepository implements BookRepository{
}
BBookRepository.java
@Repository
public class BBookRepository implements BookRepository{
}
실행 후 결과는 다음과 같다.
Field bookRepository in com.springstudy.demoppring51.BookService required a single bean, but 2 were found:
- ABookRepository: defined in file [/Users/hongsungmoon/Desktop/Dev/SpringStudy/demospring51/target/classes/com/springstudy/demoppring51/ABookRepository.class]
- BBookRepository: defined in file [/Users/hongsungmoon/Desktop/Dev/SpringStudy/demospring51/target/classes/com/springstudy/demoppring51/BBookRepository.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
이러한 경우
1. @Primary Annotation을 사용하여 해결
- 여러개의 Bean이 존재할 경우 Primary Annotation가 붙은 Bean을 주입하도록 표시
ABookRepository.java
@Repository @Primary
public class ABookRepository implements BookRepository{
}
2. @Qualifier Annotation을 사용하여 해결
- 주입하고 싶은 Bean의 이름을 직접 입력
BookService.java
@Service
public class BookService {
@Qualifier("aBookRepository")
BookRepository bookRepository;
}
이 방법 보다는 1번 방법이 조금 더 Type Safe 하므로 추천
3. 해당 타입의 모든 Bean 주입
BookService.java
@Service
public class BookService {
@Autowired
List<BookRepository> bookRepositories;
}
위의 방법들 중 1번 방법을 가장 추천한다.
@Autowired 동작 원리
BeanPostProcessor라는 LifeCycle Interface의 구현체에 의해 동작한다.
BeanPostProcessor
- Bean의 instance를 만든 후 Bean의 초기화 LifeCycle(Bean initialization Life Cycle)이 존재.
- Bean initialization Life Cycle 이전과 이후에 또 다른 부가적인 작업을 할 수 있는 LifeCycle CallBack
Example 1. PostConstruct
@Service
public class BookService {
@Autowired
BookRepository bookRepository;
@PostConstruct
public void setUp() {
// Do Something..
}
}
Example 2. InitializingBean
@Service
public class BookService implements InitializingBean {
@Autowired
BookRepository bookRepository;
@Override
public void afterPropertiesSet() throws Exception {
// Do Something..
}
}
Spring IoC Container와 Bean 에서 정리한 Bean 초기화 메소드 및 표준 순서의 11-14번은 다음과 같다.
11. BeanPostProcessors의 postProcessBeforeInitialization 메소드
12. InitializingBean의 afterPropertiesSet
13. 커스텀 init-method 정의
14. BeanPostProcessors의 postProcessAfterInitialization 메소드
이러한 postProcessBeforeInitialization, postProcessAfterInitialization 2가지의 Callback 메서드를 더 제공해 주는데 이 중에서 AutowiredAnnotationBeanPostProcessor가 동작해서 @Autowired 라는 Annotation을 찾아 해당 타입의 Bean을 주입해 준다.
Bean의 initialization 전에!! => postProcessBeforeInitialization에서 해준다.
따라서 위의 PostConstruct Annotation을 통한 작업은 postProcessAfterInitialization 단계에서 동작한다.
조금 더 자세한 동작 원리…
BeanFactory(Application Context)가 자신에게 등록되어 있는 BeanPostProcessor를 찾는다.(AutowiredAnnotationBeanPostProcessor)
일반적인 Bean들에게 BeanPostProcessor에 있는 Autowired Annotation을 처리하는 로직을 적용한다.
AutowiredAnnotationBeanPostProcessor 는 기본적으로 등록이 되어있다.
AutowiredAnnotationBeanPostProcessor가 등록되어있는지 확인하는 방법
ApplicationRunner를 만들어서 확인
MyRunner.java
@Component
public class MyRunner implements ApplicationRunner {
@Autowired
ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
AutowiredAnnotationBeanPostProcessor bean =
applicationContext.getBean(AutowiredAnnotationBeanPostProcessor.class);
System.out.println(bean);
}
}
Runner를 만든 후 Application을 실행하면 다음과 같은 결과를 얻을 수 있다.
...
2019-05-21 21:00:08.357 INFO 68188 --- [ main] c.s.d.DemoSpring51Application : Started DemoSpring51Application in 2.987 seconds (JVM running for 3.992)
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@4ced35ed
Project Repository
- https://github.com/Seongmun-Hong/SpringStudy
Reference
- https://www.inflearn.com/course/spring-framework_core