김영한님의 스프링 핵심 원리 - 기본편 강의 내용 중 '7강. 의존관계 자동 주입'에 대한 내용을 정리해봤다.
직전 강의 '6강. 컴포넌트 스캔'에서는 빈을 직접 등록 하지 않고, @ComponentScan, @Component 어노테이션을 사용해서 자동으로 빈을 등록하는 방법에 대해 다루었다.
(*관련 글: https://yunb2.tistory.com/35)
이번 강의에서는 다양한 의존관계를 자동 주입하는 방법과 장단점 등에 대해 다루고있다.
의존관계 주입 방법은 크게 4가지가 있다.
빈을 생성할 때 생성자를 통해 의존관계를 주입하는 방식이고, 다음과 같은 대표적인 특징을 가진다.
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired // @Autowired는 생성자가 1개 있으면 생략 가능하다.
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
빈을 생성할 때 수정자(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;
}
}
필드에 바로 의존관계를 주입하는 방식이고, 사용하기 편하다는 장점 말고는 단점이 많아 사용하지 않는 것이 좋다. 가장 치명적인 단점은 외부에서 변경이 불가능해 테스트를 하기 힘들다는 점이다.
@Component
public class OrderServiceImpl implements OrderService {
@Autowired
private MemberRepository memberRepository;
@Autowired
private DiscountPolicy discountPolicy;
}
일반 메소드를 통해 의존관계를 중비하는 방식이고, 한번에 여러 필드를 주입 받을 수 있다는 특징이 있다. 일반적으로 잘 사용하지 않는 방식이다.
@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
}
}
🌟 항상 생성자 주입 방식을 사용하자! 🌟
(단, 중간에 의존관계 수정이 필요하다면 수정자 주입 방식을 함께 사용하자)
lombok을 사용하면 어노테이션 기반으로 많은 코드를 간단하게 생성 가능하다. 이 글에서 lombok의 적용 방법 등은 생략하고 의존관계 주입과 관련된 부분만 정리해 보았다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
}
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
}
@Component
public class FixDiscountPolicy implements DiscountPolicy { }
@Component
public class RateDiscountPolicy implements DiscountPolicy { }
...
@Autowired
private DiscountPolicy discountPolicy;
...
NoUniqueBeanDefinitionException: No qualifying bean of type 'hello.core.discount.DiscountPolicy' available: expected single matching bean but found 2: fixDiscountPolicy,rateDiscountPolicy
스프링 빈을 수동으로 등록하는 경우 2개의 빈 중 어떤 빈으로 등록할지 지정해주면 되지만, 의존 관계 자동 주입 방식에서는 이를 어떻게 해결하는지 알아보자.
@Autowired
private DiscountPolicy rateDiscountPolicy; // 필드명을 빈 이름으로 변경
@Component
@Qualifier("mainDiscountPolicy")
public class RateDiscountPolicy implements DiscountPolicy { }
@Component
@Qualifier("fixDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy { }
@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의 단점은 위와 같이 모든 코드에 @Qualifier를 붙여줘야 한다는 점이다.
@Component
@Primary
public class RateDiscountPolicy implements DiscountPolicy { }
@Component
public class FixDiscountPolicy implements DiscountPolicy { }
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Autowired
public DiscountPolicy setDiscountPolicy(DiscountPolicy discountPolicy) {
this.discountPolicy = discountPolicy;
}
@Primary는 기본값처럼 동작하고, @Qualifier는 상세하게 동작한다. 그리고 스프링은 자동보다는 수동이 더 높은 우선 순위를 가진다. 따라서 @Qualifier가 우선권이 더 높다.
이 강의에는 의존관계 주입 방식 뿐만 아니라, 수동과 자동에 관한 올바른 실무 운영 기준 Tip에 대한 내용도 포함되어 있는데 이를 정리하면 다음과 같다.
의존관계 주입에 대한 깔끔한 정리네요! 잘 보았습니다!!