@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;
}
...
생성자 주입은 다른 방법과는 달리 스프링 빈이 등록될 때 자동 주입이 일어난다.
2. 수정자 주입(setter 주입)
"선택, 변경" 가능성이 있는 의존관계에 사용된다.
스프링 빈에 등록이 안되어 있을 때도 주입할 수 있다. (주입할 대상이 없어도 동작할 수 있음)
(잘 하지는 않지만 중간에 구현체를 바꾸고싶다면 외부에서 호출함으로써 변경 가능하다.)
@Component
public class OrderServiceImpl implements OrderService {
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
@Autowired(required = false)
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;
}
...
자동 의존관계 주입은 스프링 컨테이너가 관리하는 스프링 빈이어야 동작한다.
public class AutowiredTest {
@Test
void AutowiredOption() {
ApplicationContext ac = new AnnotationConfigApplicationContext(TestBean.class);
}
static class TestBean {
@Autowired(required = false)// 메서드 자체가 호출 안됨
public void setNoBean1(Member noBean1) {
System.out.println("noBean1 = " + noBean1);
}
@Autowired//null
public void setNoBean2(@Nullable Member noBean2) {
System.out.println("noBean2 = " + noBean2);
}
@Autowired//Optional.empty
public void setNoBean3(Optional<Member> noBean3) {
System.out.println("noBean3 = " + noBean3);
}
}
}
생성자 주입을 사용해라
불변
대부분의 의존 관계는 앱 종료 전까지 변하면 안된다.
수정자 주입을 사용하게되면 set함수를 public으로 열어둬야 하기때문에 누군가 변경할 수 있다.
생성자 주입은 객체 생성 시 딱 딱 1번만 호출되기 때문에 불변하게 설계할 수 있다.
누락
수정자 주입을 사용할 경우 프레임워크 없이 순수한 자바 코드를 단위 테스트 하는 경우 의존 관계를 자세히 알 수 없다.(알려면 직접 코드를 봐야한다)
final 키워드를 사용할 수 있다.
final은 생성할 때 정해지면 바뀌지 않고, 직접 값을 넣거나 생성자에서만 값을 줄 수 있다.
그래서 생성자에서 혹시라도 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.
최근에는 생성자를 딱 1개 두고 @Autowired를 생략하는 방법을 주로 사용한다. 여기서 롬복을 사용하면 코드가 간결해진다.
롬복이 자바의 어노테이션 프로세서라는 기능을 사용하여 컴파일 시점에 생성자 코드를 자동으로 생성해준다.
@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// @Autowired //LOMBOK @RequiredArgsConstructor가 대신 써주는 코드
// public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
// this.memberRepository = memberRepository;
// this.discountPolicy = discountPolicy;
// }
...
이름만 다르고, 완전히 똑같은 스프링 빈이 2개 있을 때 해결방법은 다음과 같다.
@Autowired
private DiscountPolicy discountPolicy
아래 코드로 바꿔주면 정상으로 돌아간다.
@Autowired
private DiscountPolicy fixDiscountPolicy
@Component
@Qualifier("mainDiscountPolicy")
public class FixDiscountPolicy implements DiscountPolicy { ... }
@Component
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
@Autowired
public OrderServiceImpl(MemberRepository memberRepository, @Qualifier("mainDiscountPolicy") DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
...
만약에 Qualifier 이름을 못찾으면 그 이름의 스프링 빈을 추가로 찾는다.
@Qualifier("xxx")를 사용할 때, 문자는 컴파일에서 체크할 수 없다.(ex 오타) 어노테이션을 직접 만들어서 사용하면 경고가 뜨기때문에 오류를 찾기 쉽다.
기술 지원 로직들은 수동 빈 등록을 사용하여 명확하게 들어내는 것이 좋다.