- 해당 게시물은 인프런 "스프링 핵심 원리 - 기본편" 강의를 참고하여 작성한 글입니다.
- 자세한 코드 및 내용은 강의를 참고해 주시길 바랍니다.
강의링크 -> 스프링 핵심 원리 - 기본편 (김영한)
의존 관계 주입은 생성자 주입, 수정자 주입(setter 주입), 필드 주입, 일반 메서드 주입 으로 크게 4가지 방법이 있다.
@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를 생략해도 자동 주입이 된다.
@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 애노테이션을 가진 매서드를 자동으로 실행하기 때문에 setter메서드를 따로 실행하지 않아도 자동으로 주입된다.
@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;
}
}
과거에는 수정자 주입과 필 드 주입을 많이 사용했지만, 최근에는 스프링을 포함한 DI 프레임워크 대부분이 생성자 주입을 권장한다.
주입할 스프링 빈이 없어도 동작해야 할 때가 있다.
@Autowired(required=false)
: 자동 주입할 대상이 없으면 수정자 메서드 자체가 호출 안된다
org.springframework.lang.@Nullable
: 자동 주입할 대상이 없으면 null이 입력된다
Optional<>
: 자동 주입할 대상이 없으면 Optional.empty 가 입력된다
Lombok
이란 Java
라이브러리로 반복되는 getter
, setter
, toString
.. 등의 반복 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리이다
@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;
}
기존의 OrderServiceImple
코드를 Lombok
을 이용해서 최적화해보자
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@RequiredArgsConstructor
: final이나 @NotNull 이 붙은 필드의 생성자를 자동 생성해주는 애노테이션
최근에는 생성자를 1개 두고, @Autowired
를 생략하는 방법을 주로 사용한다. 여기에 @RequiredArgsConstructor
를 함께 사용하면 깔끔하게 코드를 작성할 수 있다. 가끔 생성자가 필요할 때도 있는데 그 경우에만 생성자를 직접 만들도록 하자.
@Autowired
는 빈을 타입으로 조회하는데 선택된 빈이 2개 이상일 때 문제가 발생한다. DiscountPolicy
의 하위 타입인 FixDiscountPolicy
, RateDiscountPolicy
둘 다 스프링 빈으로 선언하는 경우 NoUniqueBeanDefinitionException
오류가 발생하는데 이를 해결 할 수 있는 3가지 방법이 있다.
의도적으로 해당 타입의 스프링 빈이 다 필요한 경우도 있다. 예를 들어 클라이언트가 할인 서비스로 할인의 종류(rate, fix)를 선택할 수 있다고 가정하자.
static class DiscountService {
private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;
// @Autowired 생략가능
public DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
this.policyMap = policyMap;
this.policies = policies;
}
public int discount(Member member, int price, String discountCode) {
DiscountPolicy discountPolicy = policyMap.get(discountCode);
System.out.println("discountCode = " + discountCode);
}
}
DiscountService
를 AutoAppConfig
와 같이 스프링 빈으로 등록하면 DiscountPolicy
타입인 DiscountPolicy
, RateDiscountPolicy
모두 Map
형태로 policyMap
에 들어가게 되고 이를 이용하면 상황에 맞는 할인 서비스를 제공할 수 있게 된다.