일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- sql
- Union-find
- set
- string
- CSS
- Calendar
- List
- 큐
- 리소스모니터링
- 스택
- deque
- 힙덤프
- map
- alter
- spring boot
- priority_queue
- GC로그수집
- date
- 스프링부트
- javascript
- Properties
- JPA
- NIO
- union_find
- BFS
- scanner
- dfs
- math
- html
- Java
- Today
- Total
매일 조금씩
[IoC 컨테이너와 빈] 3. @Autowired 본문
[전체 목차]
- IoC 컨테이너와 빈
- 스프링 IoC 컨테이너와 빈
- ApplicationContext와 다양한 빈 설정 방법
- @Autowired
- Resource / Validation
- 데이터 바인딩
- SpEL
- 스프링 AOP
- Null-Safety
1. @Autowired
: 필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입한다.
기본값은 true (못 찾으면 애플리케이션 구동 실패)
2. 사용할 수 있는 위치
1) 생성자 (스프링 4.3부터는 @Autowired 생략 가능)
@Service
클래스의 생성자에 @Autowired
를 안붙여도 Spring 4.3 부터는 의존성 주입을 해준다.
@Repository
가 붙어야할 리파지터리 클래스에는 @Component
가 아닌 @Repository
를 붙여주는 것이 좋다.
그래야 @Repository
가 붙은 모든 빈의 특정 기능을 실행시킬 수도 있고, AOP 에서 사용하기 더 좋다.
>> @Repository 가 @Component 보다 AOP 에서 사용하기 더 좋은 이유는?
@Repository
어노테이션이 붙은 클래스는 Spring AOP의 예외 변환 기능을 통해 데이터베이스 예외를 DataAccessException으로 일관되게 변환할 수 있기 때문이다.- AOP가 데이터 접근 계층에서 발생하는 예외를 변환해주기 때문에, 다른 계층에서 예외를 쉽게 처리할 수 있다.
- 이는 예외가 데이터 접근 계층의 세부 구현에 종속되지 않고, 일관된 방식으로 처리되도록 도와준다.
물론, 위 사진의 코드처럼@Repository
는 @Component
가 붙은 인터페이스라서, @Component
와 같은 기능을 하는 하위 클래스 느낌이지만, @Repository
에 특화된 데이터베이스 예외 기능 같은 걸 이용하기 위해서 @Repository
처럼 구체화된 걸 쓰는게 좋다.
2) 세터, 필드
의존성 주입은 생성자 말고, 세터와 필드에서도 할 수 있다.
필드로 넣어둔 다른 빈을 해당 필드를 set하는 메서드에 @Autowired
를 달면 의존성 주입이 된다.
필드의 경우 필드 위에 달면 된다.
3) 정리
이렇게 Spring에서 의존성 주입은 생성자, 필드, 세터에 @Autowired
를 붙이면 할수 있다.
그러나, Spring에서 가장 권장하는 방법은 생성자를 사용한 방법이다.
왜냐하면 생성자를 사용하는 방법이 ~
- final 필드를 사용할 수 있어, 의존성 주입 후 해당 필드를 변경할 수 없어 안정성이 높고,
- 생성자로 의존성을 전달 받아서, 테스트 시에 쉽게 mock 객체나 테스트용 객체를 주입할 수 있으며,
- 생성자를 통해 의존성을 주입하므로, 코드만 보고도 해당 클래스가 어떤 의존성을 갖는지 생성자만 보면 명확히 알수있고,
- Spring에 가장 덜 의존적이기 때문이다.
4번의 Spring에 의존적이라는 말은 ~
@Autowired
는 스프링에서 제공하는 의존성 주입용 어노테이션이다.
따라서, Spring의 경우 @Autowired
를 사용한 의존성 주입을 제공하기 때문에
필드와 세터를 통한 의존성 주입처럼 @Autowired
를 써야만 의존성 주입이 된다면, Spring에 의존하는 것이 된다.
그러나, 생성자를 이용하는 방법은 프레임워크(Spring) 없이 Java만으로 의존성을 주입할 수 있다.
이는 Java의 기본적인 객체 생성 방식이므로, 의존성 주입의 원형이기도 하다.
다만, Spring IoC 컨테이너는 이런 생성자를 통한 주입을 자동화하여 객체 생성과 의존성 관리를 더 편하게 해주는 역할을 한다.
또한, Spring4.3에서 부턴 생성자가 하나일때,@Autowired
를 생략할 수 있기 때문에 더욱 편리해졌다.
생성자 방법의 유일한 단점인 코드의 길이가 길다는 것 또한, Lombok를 사용하여 커버 가능해졌다.
@RequiredArgsConstructor
or @AllArgsConstructor
를 쓰면 된다.
@RequiredArgsConstructor
의 경우, 필드에 final 키워드를 쓰거나, @NonNull
를 붙이면 적용 되고,
@AllArgsConstructor
의 경우, 모든 필드에 적용 된다.
3. 주입할 빈에 따른 경우의 수
- 해당 타입의 빈이 없는 경우 (required = false 하면 에러 안남)
- 해당 타입의 빈이 한개인 경우
- 해당 타입의 빈이 여러개인 경우
- 빈 이름으로 시도, ('@Primary', '@Qualifier', '빈 클래스 이름 직접 언급' 등의 방법 사용)
- 같은 이름의 빈 찾으면 해당 빈 사용
- 같은 이름의 빈 못 찾으면 실패
- 빈 이름으로 시도, ('@Primary', '@Qualifier', '빈 클래스 이름 직접 언급' 등의 방법 사용)
>> 생성자를 사용해서 의존성 주입을 할땐 주입할 빈이 없으면 현재 빈을 생성하는 것에도 에러가 나겠지만..세터를 사용하면 현재 빈을 생성하는거 자체엔 에러가 안나야하는 것 아닌가??
아니다. @Autowired
라는 어노테이션이 있다면 해당 빈을 생성할 때, 이 어노테이션이 붙은 걸로 의존성 주입을 하려고 시도한다.
따라서, @Autowired
때문에 의존성 주입할 빈이 없으면 현재 빈생성도 실패한다.
만약 해당 의존성이 없어도 되면 @Autowired
옵션으로 required=false를 주면된다.
4. 같은 타입의 빈이 여러개 일때 (인터페이스 구현체 클래스들)
1) @Primary
인터페이스 그 자체를 @Autowired
로 의존성을 주입 하는건 불가능하다.
만약 하나의 인터페이스에서 여러개의 구현체 빈들이 있다면, 어떤 빈이 우선적으로 다른 빈에 주입이 되게 할지,
구현체 중 하나의 어노테이션 '옆에' @Primary
를 붙여줘야한다.
ex) @Repository
@Primary
2) @Qualifier("")
혹은, 의존성 주입시,
@Autowired
옆에 @Qualifier("주입할 구현체의 소문자 시작 카멜케이스 이름")
를 붙이면 된다.
그러나 @Primary
가 좀더 타입 세이프해서 더 추천한다.
>> 왜 @Primary
가 더 타입 세이프할까?
@Primary
는 타입 일치만으로 빈을 선택하므로, 컴파일 시점에 타입 오류를 쉽게 발견할 수 있지만, @Qualifier
는 문자열로 빈 이름을 개발자가 직접 지정하기 때문에 문자열 오타나 잘못된 빈 이름 지정 시 런타임 오류가 발생할 수 있다.
3) 구현체 이름을 직접 언급
잘 추천하진 않는 방법인데..
여러개의 빈 중 하나의 빈 클래스 이름을 넣어서 주입 받을 수도 있다. 아래와 같이..
@Autowired
BookRepository myBookRepository;
@Primary
를 가장 추천한다.
4) 여러개를 모두 주입 받아야하는 경우엔?
@Autowired
붙이는 필드에 List
타입으로 선언하면 된다.
5. 동작 원리
@Autowired
의 동작 원리를 알려면 이 어노테이션을 감지하고, 의존성 주입을 수행하는
Spring 의 인터페이스(BeanPostProcessor)와
그걸 실제로 실행하는 구현체 빈(AutowiredAnnotaionBeanPostProcessor)을 알아야한다.
1) BeanPostProcessor 인터페이스
빈의 인스턴스를 만든 다음에, 빈의 initialization 라는 라이프사이클이 있는데,
그 라이프사이클 이전 또는 이후에 부가적인 작업을 할수 있는 라이프 사이클이 있다.
그게 바로 BeanPostProcessor
다.
이건 BeanFactory
인터페이스와는 별개다.
둘 다 Spring의 IoC 컨테이너가 제공하는 기능의 일부인데.
BeanPostProcessor
는 BeanFactory
와는 직접적인 상속 관계가 없으며, BeanFactory
가 관리하는 빈의 라이프사이클에 참여하는 독립적인 역할을 가집니다.
2) BeanPostProcessor
인터페이스 사용법
빈 생성 이후에 뭘 실행 시키려면,
- 빈 클래스 안에 실행시킬 메서드에 @PostConstruct를 붙이거나,
- 해당 클래스를 implements InitializingBean 하게 하고, afterProperiesSet 메서드를 클래스 안에 @Override 하면 된다.
Spring boot runner는 애플리케이션 구동 후에 실행이 돼서 주입된 빈을 출력하면 마지막에 찍히는데,
@PostConstruct
는 다음 spring 애플리케이션 구동 단계 중에서 ..
...
11. postProcessorBeforeInitialization methods of BeanPostProcessors
12. InitializingBean's afterPropertiesSet
13. a custom init-method definition
14. postProcessorAfterInitialization methods of BeanPostProcessors
...
12번 단계에서 일을 한다.
11번에서 @Autowired
로 빈 주입을 다 한다.
12번에선 빈 주입이 다 됐다고 생각하고, 초기화 단계에서 수행된다.
13번과정은 초기화과정에서 커스텀된게 있다면 실행하는데, 12번이랑 비슷하지만 12번이 먼저 동작한다.
여기서 말해주는 라이프사이클 정도만 알아도 크게 지장없다.
BeanPostProcessor,
AutowiredAnnotaionBeanPostProcessor extends BeanPostProcessor
이렇게 두 인터페이스가 핵심.
3) 정리하자면,,,
빈 라이프 사이클에
BeanPostProcessor
라는 게 있구나!
그리고 그 구현체 중 하나인AutowiredAnnotaionBeanPostProcessor
가 동작을 하는구나!
4) 이 구현체가 어떻게 동작을 하느냐! 동작 순서를 보자!
- ApplicationContext가 BeanFactory 역할을 수행하며, 모든 빈을 생성하고 관리하는 IoC 컨테이너 역할을 하는데.
- 빈을 생성할 때마다 내부에 등록된 BeanPostProcessor 빈들을 찾는다.
- 그중에 하나로 @Autowired 와 같은 어노테이션을 처리하는 AutowiredAnnotaionBeanPostProcessor라는 빈 후처리기
또한 빈으로 포함 되어있다. - ApplicationContext는 이렇게 초기화 과정에 AutowiredAnnotationBeanPostProcessor를 개입시켜 구동시킨다.
- AutowiredAnnotationBeanPostProcessor는 모든 빈에 등록된 @Autowired를 감지하여 의존성을 주입한다.
- 그리고 다른 일반적인 빈들한테 이걸 적용하는 거다.
- 예를 들어, UserService 빈의 필드에 @Autowired 가 붙어 있으면, AutowiredAnnotationBeanPostProcessor 는 같은 타입의 빈을 찾아 UserService 빈에 주입한다.
그러니까 기본적으로 AutowiredAnnotaionBeanPostProcessor
얘도 빈으로 등록이 되어있다는 것!
5) claud.ai가 그려준 Spring Framework의 핵심 아키텍처
그림 그리는건 chatGPT 보다 훨~~~씬 나은거 같다.
점선은 의존성 주입을 표한한 것이다.
- Configuration 메타데이터: 빈 설정 정보를 제공 (어노테이션, XML, 자바 설정).
- ApplicationContext: Configuration 메타데이터를 읽고, 빈을 생성하고 등록.
- AutowiredAnnotationBeanPostProcessor: 빈 초기화 단계에서 @Autowired를 처리하여 의존성을 주입.
- 빈 생명주기 관리: ApplicationContext가 빈의 생명주기를 관리하고, 초기화와 종료 작업을 수행.
>> 테스트 중에 spring boot runner를 썼던 이유는?
@SpringBootApplication
을 쓰다보니 의존성 주입 된 빈 출력하려면 빈등록 과정에서 빼와서 찍었는데 그걸 저 어노테이션이 다 해줘서 spring boot runner 써서 구동 이후에 ApplicationContext
에서 빈을 꺼내와서 찍도록 했음.
ApplicationContext
도 Spring 애플리케이션에서 빈으로 등록된다.
사실 ApplicationContext
는 자체적으로 빈을 관리하고 제공하는 컨테이너이지만,
그 자체도 하나의 빈(BeanFactory 의 하위 클래스)으로 등록되어 다른 빈들이 필요할 때 주입받아 사용할 수 있다.
>> 모든 빈들은 @Autowired
로 다 꺼내와서 의존성 주입을 받을 수 있다.
'Spring Framework > 스프링 프레임워크 핵심 기술' 카테고리의 다른 글
[IoC 컨테이너와 빈] 5. 빈의 스코프 (0) | 2024.11.06 |
---|---|
[IoC 컨테이너와 빈] 4. @Component 와 컴포넌트 스캔 (1) | 2024.11.04 |
[IoC 컨테이너와 빈] 2. ApplicationContext와 다양한 빈 설정 방법 (0) | 2024.11.03 |
[IoC 컨테이너와 빈] 1. 스프링 IoC 컨테이너와 빈 (0) | 2024.11.03 |