7. 의존관계 자동 주입
다양한 의존관계 주입 방법
- 생성자 주입
- 수정자 주입(setter 주입)
- 필드 주입
- 일반 메서드 주입
생성자 주입
특징
- 생성자 호출 시점에 딱 1번만 호출되는 것이 보장됨
- 불변, 필수 의존관계에 사용
불변: 생성자 외에 외부에서 의존관계를 수정할 수 없음
필수: final, 꼭 값이 있어야 함
생성자가 딱 1개만 있으면 @Autowired
를 생략해도 자동으로 지정됨
생성자 주입은 빈을 등록하면서 의존관계 주입도 같이 일어남 - 생성자를 호출하게 되기 때문에
수정자 주입
setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해 주입
특징
- 선택, 변경 가능성이 있는 의존관계에 사용
참고: @Autowired(required = false)
→ 주입할 대상이 없어도 동작하게 할 때
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법
자바빈 프로퍼티 규약: setter, getter 메서드를 통해 값을 수정/읽음
필드 주입
필드에 바로 주입하는 것
특징
- 코드가 간결, 외부에서 변경이 불가능해 테스트하기 힘들다
스프링이 아닌 그냥 자바에서 실행할 경우 테스트가 어려움
- DI 프레임워크가 없으면 아무것도 할 수 없음
- 사용을 권하지 않음, 사용 가능한 곳?
- 애플리케이션의 실제 코드와 관계 없는 테스트 코드
- 스프링 설정을 목적으로 하는 @Configuration과 같은 곳에서 사용
일반 메서드 주입
특징
- 한번에 여러 필드를 주입받을 수 있음
- 일반적으로 잘 사용하지 않는다
의존관계 자동 주입은 스프링 빈이어야 작동함
옵션 처리
주입할 스프링 빈이 없어도 동작해야할 때가 있음
@Autowired만 사용하면 required 옵션 기본값이 true
@Autowired(required=false)
메서드 자체가 호출되지 않음
@Autowired
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
호출은되는데 null
로 입력됨
@Autowired
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
noBean3 = Optional.empty
생성자 주입을 선택해라!
“불변”
- 대부분 의존관계 주입은 한 번 일어나면 애플리케이션 종료 시점까지 의존관계를 변경할 일이 없고, 대부분 애플리케이션 종료 전까지 변하면 안된다
- 수정자 주입을 사용하면 setXxx 메서드를 public으로 열어둬야함 → 변경이 가능할 수 있음 → 좋은 방식이 아님
- 생성자 주입은 객체를 생성할 때 1번만 호출되므로 이후에 호출되는 일이 없다 → 불변하게 설계 가능
“누락”
생성자 주입 → final
키워드를 추가할 수 있음 → 코드 누락할 경우 초기화가 되지 않기 때문에 compile 오류로 쉽게 잡을 수 있음
수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되기 때문에 필드에 final 키워드를 사용할 수 없음
오직 생성자 주입 방식만 final 키워드를 사용할 수 있다
롬복과 최신 트렌드
필드 주입처럼 편리하게 사용하는 방법?
설정 - annotation processors → enable 켜주기
@RequiredArgsConstructor
// final이 붙은 변수를 가지고 생성자를 만들어줌
의존관계 추가할 때 편함
최근에는 생성자를 1개 두고, @Autowired
를 생략하는 방법을 주로 사용
Lombok 라이브러리를 사용하면 더 깔끔하게 가능함
조회 빈이 2개 이상 - 문제
fixed discount, rate discount 두 개 모두 component로 등록하면 NoUniqueBeanDefinitionException
오류 발생
하위타입으로 지정하면 DIP 위배, 유연성이 떨어짐
이름만 다르고 완전히 똑같은 타입의 스프링 빈이 2개 있을 때 해결이 안됨
수동 등록해서 해결해도 되지만, 의존 관계 자동 주입에서 해결할 수 있는 방법이 있음
@Autowired 필드 명, @Qualifier, @Primary
@Autowired
@Autowired
는 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭함
생성자의 파라미터 이름에 따라서 빈 이름을 매칭함
- @Autowired 정리
- 타입 매칭
- 타입 매칭의 결과가 2개 이상일 때 필드 명, 파라미터 명으로 빈 이름 매칭
@Qualifier
추가 구분자, 빈 이름 자체를 변경하는 것은 아님, 주입시 추가적인 방법을 제공
생성자, 수정자 모두 가능
Qualifier 이름을 못 찾으면 그 이름의 스프링 빈을 추가로 찾음
@Qualifier를 찾는 용도로만 사용하는 게 명확하고 좋다
- Qualifier끼리 매칭
- 빈 이름 매칭
- NoSuchBeanDefinitionException 발생
@Primary
우선순위를 정하는 방법
@Primary
가 우선권을 가진다
Qualifier의 단점: 모든 코드에 Qualifier를 붙여줘야 한다
“우선순위”
@Primary는 기본값처럼 동작, @Qualifier는 매우 상세하게 동작
Qualifier가 우선순위가 높다 (상세하게 동작하기 때문)
애노테이션 직접 만들기
애노테이션은 상속 개념이 없음
여러 애노테이션을 모아서 사용하는 기능은 스프링이 지원해주는 기능
다른 애노테이션도 재정의 가능
조회한 빈이 모두 필요할 때, List, Map
의도적으로 해당 타입의 스프링 빈이 다 필요한 경우도 있다
자동, 수동의 올바른 실무 운영 기준
편리한 자동 기능을 기본으로 사용하자
시간이 갈수록 자동을 선호
스프링 부트는 컴포넌트 스캔을 기본으로 사용
관리할 빈이 많아서 설정 정보가 커지면 설정 정보를 관리하는 것 자체가 부담이 됨
자동 빈 등록을 해도 OCP, DIP를 지킬 수 있음
수동 빈 등록은 언제?
- 업무 로직 빈: 웹을 지원하는 컨트롤러, 핵심 비즈니스 로직이 있는 서비스, 데이터 계층의 로직을 처리하는 리포지토리 등
- 기술 지원 빈: 기술적인 문제나 공통 관심사(AOP)를 처리할 때 사용, 데이터베이스 연결이나 공통 로그 처리처럼 업무 로직을 지원하기 위한 하부 기술이나 공통 기술
애플리케이션에 광범위하게 영향을 미치는 기술 지원 객체- 수동 빈으로 등록해서 설정 정보에 바로 나타나게 하는 것이 유지보수하기 좋음
다형성 활용할 때
어떤 빈들이 주입될지, 각 빈들의 이름은 무엇일지 한눈에 파악하기 어려움
수동 빈으로 등록하거나, 자동으로 할 경우 ‘특정 패키지에 같이 묶어’두면 이해하기 쉬움
빈 자동 등록을 사용하고 싶으면 구현 빈들만 따로 모아서 특정 패키지에 모아두기
'BackEnd > Spring' 카테고리의 다른 글
[Spring] 스프링 핵심 원리 기본편 9. 빈 스코프 (0) | 2022.10.28 |
---|---|
[Spring] 스프링 핵심 원리 기본편 - 8. 빈 생명주기 콜백 (1) | 2022.10.27 |
[Spring] 스프링 핵심 원리 기본편 - 6. 컴포넌트 스캔 (0) | 2022.10.23 |
[Spring] 스프링 핵심 원리 기본편 - 5. 싱글톤 컨테이너 (0) | 2022.10.21 |
[Spring] 스프링 핵심 원리 기본편 - 4. 스프링 컨테이너와 빈 (0) | 2022.10.21 |