- 생성자 주입
- 수정자 주입 (setter 주입)
- 필드 주입
- 일단 메서드 주입
특징
- 생성자 호출시점에 딱 1번만 호출되는 것이 보장된다.
- 불변, 필수 의존관계에 사용한다.
@Component public class OrderServiceImp implements OrderService{ // final 키워드 붙으면 무조건 값이 있어야 한다는 것이다. (필수) private final MemberRepository memberRepository; private final DiscountPolicy discountPolicy; // 생성자가 딱 1번만 호출된다. (불변) // 외부에서 수정할 수 없다. @Autowired public OrderServiceImp(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
🚨 생성자가 딱 1개만 있으면 @Autowired를 생략해도 자동 주입된다! 물론 스프링 빈에만 해당된다. 🚨
@Component
public class OrderServiceImp implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
// @Autowired 생략
public OrderServiceImp(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}

특징
- 선택, 변경 가능성이 있는 의존관계에 사용한다.
- 자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.
자바빈 프로퍼티란?
자바에서 setXxx, getXxx 라는 메서드를 통해 값을 읽거나 수정하는 규칙을 말한다.
@Component
public class OrderServiceImp implements OrderService{
private MemberRepository memberRepository;
private DiscountPolicy discountPolicy;
// 메서드를 통해 필드값을 수정한다.
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
System.out.println("memberRepository = " + memberRepository);
this.memberRepository = memberRepository;
}
@Autowired
public void setDiscountPolicy(DiscountPolicy discountPolicy) {
System.out.println("discountPolicy = " + discountPolicy);
this.discountPolicy = discountPolicy;
}
OrderServiceImp를 스프링 컨테이너에 등록을 하고, 연관 관계를 자동으로 주입한다. 즉, @Autowired가 붙은 부분을 자동으로 주입한다.

참고
@Autowired의 기본 동작은 주입할 대상이 없으면 오류가 발생한다.
주입할 대상이 없어도 동작하게 하려면@Autowired(required = false)로 지정하면 된다.
특징
- 코드가 간결해서 많은 개발자들을 유혹하지만 외부에서 변경이 불가능해서 테스트 하기 힘들다는 치명적인 단점이 있다.
- DI 프레임워크가 없으면 아무것도 할 수 없다.
- 사용하지 말자!
- 애플리케이션의 실제 코드와 관계 없는 테스트 코드
- 스프링 설정을 목적으로 하는
@Configuration같은 곳에서만 특별한 용도로 사용
@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;
}
}
@Autowired만 사용하면 required 옵션의 기본값이 true로 되어 있어서 자동 주입 대상이 없으면 오류가 발생한다.static class TestBean{
// 메서드 자체가 호출 안된다
@Autowired(required = false)
public void setNoBean1(Member member) {
System.out.println("member = " + member);
}
// null 호출
@Autowired
public void setNoBean2(@Nullable Member member2) {
System.out.println("member2 = " + member2);
}
// Optional.empty 호출
@Autowired
public void setNoBean3(Optional<Member> member3) {
System.out.println("member3 = " + member3);
}
}
setNoBean1()은 ```@Autowired(required=false)이므로 호출 자체가 안된다.
@Nullable,Optional은 스프링 전반에 걸쳐서 지원된다.
예를 들어 생성자 자동 주입에서 특정 필드에만 사용해도 된다.
과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근에는 대부분이 생성자 주입을 권장한다.
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가 프레임워크 안에서 동작할 때는 의존관계가 없으면 오류가 발생하지만,아래와 같이 테스트를 실행하면 실행은 된다.
@Test
void createOrder(){
OrderServiceImpl orderService = new OrderServiceImpl();
orderService.createOrder(1L, "itemA", 100000);
}
memberRepository, discountPolicy 모두 의존관계 주입이 누락되었기 때문이다.주입 데이터가 주락 되었을 때 컴파일 오류가 발생한다.
그리고 IDE에서 어떤 값을 필수로 주입해야 하는지 알 수 있다.
final 키워드를 사용할 수 있다.final 키워드를 사용할 수 있다.@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;
}
//...
}
discountPolicy가 누락되었다.java: variable discountPolicy might not have been initialized
🧙🏻♀️ 컴파일 오류는 세상에서 가장 빠르고, 좋은 오류다!
- 프레임 워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이다.
- 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다. (생성자 주입, 수정자 주입 동시 사용 가능)
- 항상 생성자 주입을 선택해라! 그리고 가끔 옵션이 필요하면 수정자 주입을 선택해라! 필드 주입은 사용하지 않는게 좋다..!
fianl 키워드를 사용하게 된다.@RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어 준다.@Component
@RequiredArgsConstructor // final이 붙은 필드 값을 가지는 생성자 코드를 그대로 만들어줌
public class OrderServiceImp implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
final 키워드로 선언된 필드를 가지고 있는 생성자를 자동 생성해준다.
생성자 1개를 두고
@Autowired를 생략하는 방법을 사용하고, 거기에@RequiredArgsConstructor를 함께 사용하여 코드를 더 간결하게 짤 수 있다.
