View
백기선님의 스프링 프레임워크 핵심 기술이라는 강좌를 들으며 공부한 내용을 정리한 글입니다.
오늘은 @Autowired Annotation의 사용 방법, 동작 원리까지 알아본다.
1. @Autowired란?
- @Autowired는 의존성 주입을 할 때 사용하는 Annotation으로 의존 객체의 타입에 해당하는 bean을 찾아 주입하는 역할을 한다.
2. @Autowired를 사용할 수 있는 위치
@Autowired는 기본적으로 아래의 위치에서 사용할 수 있다.
- 생성자 (스프링 4.3부터는 생략 가능)
- Setter
- 필드
3. 사용 시 주의점
해당 타입의 bean이 없거나 한 개인 경우
3-1. 생성자에 @Autowired 명시 (스프링 4.3부터는 생략 가능)
@Service
public class TestService {
TestRepository testRepository;
@Autowired
public TestService(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
public class TestRepository {
....
}
위의 코드에서 TestRepository의 의존성 주입이 작동할까?
당연히 작동하지 않는다. @Autowired는 의존 객체의 타입에 해당하는 bean을 찾아 주입하기 때문이다. 즉, TestRepository는 bean으로 등록되어있지 않기 때문에 스프링이 bean을 찾지 못해 의존성을 주입할 수 없다.
이를 작동하게 하기 위해서는 @Repository 혹은 @Component Annotation을 이용해
TestRepository를 bean으로 등록해주면 된다.
@Repository
public class TestRepository {
....
}
참 쉽쥬?
3-2. Setter에 @Autowired 명시
@Service
public class TestService {
TestRepository testRepository;
@Autowired
//@Autowired(required = false)
public void setTestRepository(TestRepository testRepository) {
this.testRepository = testRepository;
}
}
public class TestRepository {
....
}
이번엔 Setter를 이용해 의존성 주입을 시도해보자. 하지만 이 역시 작동하지 않는다.
setter로 TestService 자체의 인스턴스를 만들었는데 왜 작동을 안 할까?
그 이유는 @Autowired Annotation 때문이다.
@Autowired가 명시되어 있기 때문에 스프링은 의존성을 주입하려고 시도한다.
setter를 이용해 인스턴스를 생성할 수 있지만, 의존성 주입에는 실패하는 것이다.
@Autowired(required = false) 옵션을 주면 의존성 주입을 받지 않고 인스턴스를 만들어서 bean으로 등록할 수 있다. 즉, TestRepository는 의존성 주입이 되지 않은 상태로 bean이 등록된다.
3-3. 필드에 @Autowired 명시
@Service
public class TestService {
@Autowired
//@Autowired(required = false)
TestRepository testRepository;
}
public class TestRepository {
....
}
필드 위에 @Autowired 명시
setter나 필드 Injection을 사용할 때는 Optional 설정(required = false)을 통해 TestService가 해당하는 의존성 없이도 bean으로 등록할 수 있다.
해당하는 타입의 bean이 여러 개인 경우
@Service
public class TestService {
@Autowired
TestRepository testRepository;
}
public interface TestRepository {
....
}
@Repository
public class MyTestRepository implements TestRepository {
// TestRepository의 구현체
}
@Repository
public class ExampleRepository implements TestRepository {
// TestRepository의 구현체
}
TestService는 TestRepository를 사용하는데, TestRepository는 인터페이스이고 이를 구현하는
Repository bean 두 개가 존재한다고 생각해보자.
스프링은 TestService에는 어떤 Repository를 주입해줄까?
스프링은 개발자가 원하는 의존성을 알지 못하기 때문에 주입을 못해준다.
따라서 같은 타입의 bean을 두 개 발견했다는 에러 메시지를 뱉고, 다음과 같은 액션을 추천해준다.
- @Primary
- 해당 타입의 bean 모두 주입받기
- @Qulifier(bean id)
1. @Primary
@Primary Annotation은 같은 타입의 bean이 여러 개 주입될 경우 @Primary가 붙은 bean을 주입하겠다는 의미를 가지고 있다.
public interface TestRepository {
....
}
@Repository @Primary
public class MyTestRepository implements TestRepository {
// TestRepository의 구현체
}
@Repository
public class ExampleRepository implements TestRepository {
// TestRepository의 구현체
}
- @Repository 옆에 명시
- 같은 타입의 TestRepository가 주입되었을 때 MyTestRepository가 주입된다.
2. @Qulifier
@Service
public class TestService {
@Autowired @Qulifier("myTestRepository") // bean의 Id는 lower Camel Case를 사용한다.
TestRepository testRepository;
}
- @Autowired 옆에 명시
- @Primary와 마찬가지로 MyTestRepository가 주입된다.
@Primary가 @Qulifier보다 Type safe 하기 때문에 @Primary를 사용하는 것을 추천
3. 해당 타입의 bean 모두 주입
@Service
public class TestService {
@Autowired
List<TestRepository> testRepositoies;
}
- List로 같은 타입의 모든 bean을 주입받을 수 있다.
4. 동작 원리
주요 개념
- BeanPostProcessor
- 초기화 라이프 사이클 이전과 이후에 필요한 부가 작업을 할 수 있는 라이프 사이클 콜백
- IoC 컨테이너에 등록되어 있음
- AutowiredAnnotationBeanPostProcessor
- BeanPostProcessor의 구현체
- BeanPostProcessor의 구현체이므로 IoC 컨테이너에 Bean으로 등록되어있음
- InitializingBean
- 초기화 라이프 사이클
- Bean 인스턴스가 생성되는 시점
@Autowired는 BeanPostProcessor라는 라이프 사이클 인터페이스의 구현체인 AutowiredAnnotationBeanPostProcessor에 의해 의존성 주입이 이루어진다.
BeanPostProcessor는 초기화 라이프 사이클 이전과 이후에 필요한 부가 작업을 할 수 있는 라이프 사이클 콜백이다. 즉, bean이 만들어지는 시점 이전 혹은 이후에 추가적인 작업을 하고 싶을 때 사용된다.
AutowiredAnnotationBeanPostProcessor가 bean 초기화 라이프 사이클 이전(bean 인스턴스 생성 이전)에 @Autowired가 붙어 있는 bean을 찾아 주입해주는 작업을 한다.
대략적인 라이프 사이클을 보자면 다음과 같다.
BeanPostProcess (before) → InitializingBean → BeanPostProcess (after)
다시 한번 동작 순서를 정리하자면,
- BeanFactory( ApplicationContext )가 BeanPostProcessor 타입의 Bean을 찾는다.
- IoC 컨테이너에 등록되어있는 다른 일반적인 Bean에게 BeanPostProcessor를 적용한다.
- 다른 Bean에 @Autowired Annotation을 처리하는 AutowiredAnnotationBeanPostProcessor의 로직이 적용된다.
- 의존성 주입이 일어난다.
지금까지 @Autowired를 사용하면서도 이런 동작 원리를 가지고 있는지 몰랐다. 사실 지금은 깊게 알지 못해도 무방한 부분이지만, 이런 동작 원리를 알고 사용하는 것과 모르고 그냥 사용하는 것은 큰 차이가 있다고 생각한다. 시간이 지나고 경험이 쌓이면 이런 부분이 더 중요해지지 않을까?
좋은 개발자로 성장하기 위해서 이러한 부분을 탄탄히 다져야겠다는 생각이 들었다. 지금은 25분짜리 강의를 이해하고 정리하는데 하루나 걸리고 학습 진행 속도가 더디지만, 앞으로 더 좋아지겠지?
'BackEnd > Spring 핵심 기술' 카테고리의 다른 글
[스프링 핵심기술] - MessageSource (0) | 2020.06.04 |
---|---|
[스프링 핵심기술] - Bean의 스코프 (0) | 2020.05.28 |
[스프링 핵심기술] - @Component와 @ComponentScan (0) | 2020.05.17 |
[스프링 핵심기술] - ApplicationContext와 다양한 빈 설정 방법 (0) | 2020.05.10 |
[스프링 핵심기술] - Ioc 컨테이너와 Bean (1) | 2020.05.09 |