과거에는 수정자 주입과 필드 주입을 많이 사용했지만, 최근에는 스프링을 포함한 DI 프레임워크 대부분이

생성자 주입을 권장한다.

 

1. 불변

  • 대부분의 의존관계 주입은 한번 일어나면 애플리케이션 종료 시점까지 의존관계를 변경할 일이 없다. 오히려 대부분의 의존관계는 애플리케이션 종료 전까지 변하면 안된다.(불변해야 한다.)
  • 수정자 주입을 사용하면, setXxx 메서드를 public으로 열어두어야 한다.  -> 누군가 실수로 변경할 수 도 있고, 변경하면 안 되는 메서드를 열어두는 것은 좋은 설계 방법이 아니다.
  • 생성자 주입은 객체를 생성할 때 딱 1번만 호출되므로 이후에 호출되는 일이 없다. 따라서 불변하게 설계할 수 있다.

 

2. 누락

테스트 코드 작성시(순수 자바 코드)

 

생성자 주입을 사용하면 다음처럼 주입 데이터를 누락했을 때 컴파일 오류가 발생한다.
그리고 IDE에서 바로 어떤 값을 필수로 주입해야 하는지 알 수 있다.

아래 코드에서는 new OrderServiceImple에서 컴파일 오류가 발생한다.

@Test
void createOrder() {
    OrderServiceImpl orderService = new OrderServiceImpl();
    orderService.createOrder(1L, "itemA", 10000);
}

 

3. final 키워드

생성자 주입을 사용하면 필드에 final 키워드를 사용할 수 있다. 그래서 생성자에서 혹시라도 값이
설정되지 않는 오류를 컴파일 시점에 막아준다.

 

** 컴파일 오류는 세상에서 가장 빠르고, 좋은 오류다!

* 참고: 수정자 주입을 포함한 나머지 주입 방식은 모두 생성자 이후에 호출되므로, 필드에 final 키워드를
사용할 수 없다. 오직 생성자 주입 방식만 final 키워드를 사용할 수 있다.

 

 

 

4. 정리

  • 생성자 주입 방식을 선택하는 이유는 여러 가지가 있지만, 프레임워크에 의존하지 않고, 순수한 자바 언어의 특징을 잘 살리는 방법이기도 하다.
  • 기본으로 생성자 주입을 사용하고, 필수 값이 아닌 경우에는 수정자 주입 방식을 옵션으로 부여하면 된다.
  • 생성자 주입과 수정자 주입을 동시에 사용할 수 있다.
  • 항상 생성자 주입을 선택해라! 그리고 가끔 옵션이 필요하면 수정자 주입을 선택해라. 필드 주입은 사용하지 않는 게 좋다.

 

 

 

5. 롬복과 최신 트렌드

요약: 롬복 세팅 후 @RequiredArgsConstructor 사용하여 생성자 주입

  5-1) 세팅

  • 롬복 플러그인 설치
  • Settings - Compiler - Annotation Processors - Enable annotation processing 추가

  • build.gradle 코드 추가

# 프로잭트 생성시 lombok 라이브러리 추가하면 자동 생성
configurations {
	compileOnly {
		extendsFrom annotationProcessor
	}
}

dependencies {
	//lombok 라이브러리 추가 시작
    
	compileOnly 'org.projectlombok:lombok' # 프로잭트 생성시 lombok 라이브러리 추가하면 자동 생성
	annotationProcessor 'org.projectlombok:lombok'
	testCompileOnly 'org.projectlombok:lombok'
	testAnnotationProcessor 'org.projectlombok:lombok'
    
	//lombok 라이브러리 추가 끝
}

 

 

 

@RequiredArgsConstructor

- 생성자 코드를 자동으로 생성해주므로 코드가 간결해진다.

- final 변수 추가 시에 추가 작업이 필요치 않다

# 예시코드

@RequiredArgsConstructor // final이 붙으면 필수 값이됨 -> 필수 생성자를 생성해줌
@Component
public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    //@RequiredArgsConstructor가 생성해줌
//    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
//        this.memberRepository = memberRepository;
//        this.discountPolicy = discountPolicy;
//    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

 

 

정리
최근에는 생성자를 딱 1개 두고, @Autowired를 생략하는 방법을 주로 사용한다. 

여기에 Lombok 라이브러리의 @RequiredArgsConstructor 함께 사용하면 기능은 다 제공하면서, 코드는 깔끔하게 사용할 수 있다.

+ Recent posts