의존 관계 주입 방법(4가지)
- 생성자 주입
- 수정자 주입(setter 주입)
- 필드 주입
- 일반 메서드 주입
생성자 주입
- 생성자를 통해서 의존 관계를 주입하는 방법
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다
- 불변, 필수 의존 관계에 사용된다
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
- 중요! 생성자가 1개만 있으면 @Autowired는 생략 가능
수정자 주입(setter 주입)
- setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법
- 선택, 변경 가능성이 있는 의존관계에 사용된다
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
}
- @Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생 -> 주입할 대상이 없어도 동작하게 할려면@Autowired(required = false) 로 지정
필드 주입
- 이름 그대로 필드에 주입하는 방법이다
- 코드가 간결하지만 외부에서 변경이 불가능 -> 테스트 하기 힘들다는 단점이 있다
- DI 프레임워크가 없으면 사용 불가
- 사용하지 않는 걸 권장
- 애플리케이션의 실제 코드와 관계없는 테스트 코드
- 스프링 설정을 목적으로 하는 @Configuration 같은 곳에서만 특별한 용도로 사용
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
- 순수한 자바 테스트코드에는 @Autowired 동작 x -> . @SpringBootTest 처럼 스프링 컨테이너를 테스트에 통합한 경우에만 가능
일반 메서드 주입
- 일반 메서드를 통해서 주입 가능
- 한번에 여러 필드를 주입 받을 수 있다
- 일반적으로 잘 사용하지 않는다
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired
public void init(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
옵션 처리
주입할 빈이 없어도 동작해야 할때가 있다
- 자동 주입 대상을 옵션으로 처리하는 방법
- @Autowired(required=false) : 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안됨
- org.springframework.lang.@Nullable : 자동 주입할 대상이 없으면 null이 입력된다.
- Optional<> : 자동 주입할 대상이 없으면 Optional.empty 가 입력된다.
생성자 주입을 선택하라
생성자 주입을 선택하는 이유
- 불변
- 대부분의 의존관계 주입은 최초 1회 주입후 해당 의존관계가 변경될 일이 없다. (불변)
- 수정자 주입을 사용하면 setter(setXXX)를 public으로 열어두어 변경가능성이 생긴다.
- 객체 생성시점에서 최초 1회 생성자가 반드시 호출되며 이 상황에서 의존관계까지 주입하기에 불변 설계가 가능하다.
- 누락
- 프레임워크에 의존하지 않고 순수 자바로 고립 테스트를 하는 경우 수정자 의존관계 혹은 필드 의존관계인 경우 @Autowired 애노테이션이 스프링 프레임워크 내에서는 문제가 있을경우 오류를 발생하지만, 순수 자바로 짤 경우 실행은 되지만, NullPointException이 발생한다
- 생성자 주입을 사용하면 필드에 final 키워드를 사용할수 있으므로, 생성자에 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
롬복과 최신 트렌드
기본 코드
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // 생성자가 1개일 경우 생략가능
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
최종 결과 코드(롬복 라이브러리 적용)
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
롬복라이브러리 적용방법
- build.gradel에 라이브러리 및 환경 추가
- Preferences(윈도우 File Settings) plugin lombok 검색 설치 실행 (재시작)
- Preferences Annotation Processors 검색 Enable annotation processing 체크 (재시작)
- 임의의 테스트 클래스를 만들고 @Getter, @Setter 확인
조회 빈이 2개 이상 - 문제
@Autowired
private DiscountPolicy discountPolicy
- @Autowired는 타입으로 조회
- 다음 코드와 유사하게 동작한다 - > ac.getBean(DiscountPolicy.class)
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
@Component
public class RateDiscountPolicy implements DiscountPolicy {}
- 위 코드와 같이 타입으로 조회하면 선택된 빈이 2개 이상일때 문제가 발생한다
- NoUniqueBeanDefinitionException 오류가 발생
조회 대상 빈이 2개 이상일 때 해결 방법(3가지)
- @Autowired 필드 명 매칭
- @Qualifier @Qualifier끼리 매칭 빈 이름 매칭
- @Primary 사용
@Autowired 필드 명 매칭
- 필드명을 이름으로 변경
@Autowired
private DiscountPolicy rateDiscountPolicy
- 타입 매칭
- 타입 매칭 결과가 2개 이상일 경우, 파라미터 명으로 빈 이름 매칭
@Qualifier
- 빈 등록시 @Qualifier를 붙여 준다
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy {}
- 주입시에 @Qualifier를 붙여주고 등록한 이름을 적어준다.
// 생성자 자동 주입
@Autowired
public OrderServiceImpl(MemberRepository memberRepository,
@Qualifier("mainDiscountPolicy") DiscountPolicy
discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
// 수정자 자동 주입
@Autowired
public DiscountPolicy setDiscountPolicy(@Qualifier("mainDiscountPolicy")
DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
- @Qualifier끼리 매칭
- 빈 이름 매칭
- NoSuchBeanDefinitionException 예외 발생
@Primary
- @Primary 는 우선순위를 정하는 방법
- @Autowired 시에 여러 빈이 매칭되면 @Primary 가 우선권을 가진다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy {}
@Component
public class FixDiscountPolicy implements DiscountPolicy {}
Reference
스프링 핵심 원리 - 기본편 - 인프런 | 강의
스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런
www.inflearn.com
'Spring' 카테고리의 다른 글
Spring - 컴포넌트 스캔 (0) | 2023.04.14 |
---|---|
Spring - 싱글톤 컨테이너 (0) | 2023.04.08 |
Spring - BeanFactory와 ApplicationContext (0) | 2023.04.08 |
Spring - 스프링 컨테이너 (0) | 2023.04.07 |
Spring - 컨테이너와 IOC/DI (0) | 2023.03.24 |