Spring

@Autowired - 의존관계 주입

꾸준함. 2021. 5. 18. 19:10

의존관계 주입 방법

  1. 생성자 주입
  2. setter 주입
  3. 필드 주입
  4. 일반 메서드 주입

* 주의할 점:  스프링 컨테이너가 관리하는 스프링 빈이어야 @Autowired 어노테이션을 통해 의존관계가 자동 주입됨

 

생성자 주입 방법 (Constructor Injection)

  • 생성자를 통해서 의존 관계를 주입받는 방법
  • 생성자 호출 시점에 딱 한 번만 호출되는 것이 보장
  • 불변, 필수 의존관계에 사용 (대부분 불편, 필수 의존관계)
  • 따라서, 웬만하면 생성자 주입 방법을 사용할 것을 권장 (하단에 부연설명 추가)
  • 생성자가 하나만 정의되어 있고 스프링 빈이라면 @Autowired 어노테이션 생략 가능

* 생성자 주입 방법에만 의존관계 주입받을 필드에 final 키워드 사용 가능

 

생성자 주입 방법 1

 

 

 

생성자 주입 방법 2 (Lombok 이용)


 

Setter 주입 방법 (Setter Injection)

  • 필드 값을 변경하는 setter 메서드를 통해 의존관계를 주입받는 방법
  • 선택, 변경 가능성 있는 의존관계에 사용 (특수한 상황)
  • Java Bean 프로퍼티 규약의 Setter 메서드 방식을 사용

 

 

 

필드 주입 방법 (Field Injection)

  • 필드에 바로 주입하는 방법
  • 코드가 간결해진다는 장점이 있지만,
  • 외부에서 변경이 불가하여 테스트하기 힘들다는 단점 존재
  • DI 프레임워크 없이 독단적으로 사용 불가능하기 때문에 가급적 사용 자제
  • 여담이지만 인턴 생활을 할 때도 멘토님들이 필드 주입 방법은 사용하지 말라고 권고하셨음
  • 사용해도 괜찮은 경우
    • 애플리케이션의 실제 코드에 영향을 주지 않는 테스트 코드

 

 

 

일반 메서드 주입 방법 (Method Injection)

  • 일반 메서드를 통해 주입받는 방법
  • 한 번에 여러 필드를 주입받을 수 있음
  • 사용하는 경우 거의 없음

 

옵션 처리

  • 주입할 빈이 없더라도 동작해야 하는 경우 존재
  • @Autowired 어노테이션은 required 키워드의 디폴트 값이 true이므로 자동 주입 대상이 없을 경우 오류 발생시킴
  • 아래와 같이 세 가지 방법으로 오류 방지 가능
    1. @Autowired(required=false): 자동 주입할 대상이 없으면 setter 메서드 호출 X
    2. org.springframework.lang.@Nullable 어노테이션: 자동 주입할 대상이 없으면 NULL 입력
    3. Java8 Optional <>: 자동 주입할 대상이 없으면 Optional.empty 입력

* 1번을 제외한 2번, 3번의 경우 생성자 자동 주입 내 특정 필드에서도 사용 가능


 

생성자 주입 방법 (Constructor Injection)을 사용해야 하는 이유

  • 대부분의 의존관계 주입은 한번 일어날 경우 애플리케이션 종료시점까지 의존관계를 변경할 일이 없음
  • 생성자 주입은 객체를 생성할 때 딱 한 번만 호출되므로 불변하게 설계 가능
  • Setter 주입을 사용할 경우, setter 메서드를 public으로 둬야 하는데, 이럴 경우 누군가 실수로 변경할 수 있기 때문에 오류에 취약함
  • 프레임워크 없이 순수 자바 코드를 통해 단위 테스트를 하는 경우 생성자 주입 방법은 의존관계가 누락되었을 경우 바로 컴파일 에러를 통해 바로 캐치할 수 있지만 setter 주입 방법은 실행 후 NullPointerException이 발생함에 따라 누락됐음을 확인 가능
  • 또한, 생성자 주입을 사용할 경우 필드에 final 키워드를 사용함으로써 생성자에서 값이 설정되지 않을 경우 컴파일 에러를 통해 바로 확인 가능

* 정리하자면, 프레임워크에 의존하지 않고, 순수 자바 언어의 특징을 제일 잘 살리는 방법이 생성자 주입 방법

 

의존관계 주입할 때 조회되는 빈이 두 개 이상일 경우?

  • @Autowired 어노테이션은 Type으로 조회
  • 즉, 상위 Type을 상속받는 하위 Type 클래스가 여러 개 있는 상태에서 상위 Type으로 조회할 경우 NoUniqureBeanDefinitionException 예외를 발생시킴
  • 해당 문제를 하위 Type으로 조회하면서 해결할 수도 있지만 이는 SOLID 원칙 중 DIP를 위반
  • NoUniqueBeanDefinitionException 해결 방법은 아래와 같이 크게 세 가지
    1. @Autowired 필드명 매칭
    2. @Qualifier 어노테이션 사용
    3. @Primary 어노테이션 사용

 

@Autowired 필드명 매칭

  • @Autowired 어노테이션은 Type 매칭을 시도하고, 여러 Bean이 존재할 경우 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭
  • 정리하자면, 1순위로 Type 매칭을 시도하고 여기서 같은 Type이 여러 개라면 필드 명, 파라미터 명으로 매칭 시도


 

@Qualifier 어노테이션 사용

  • 추가 구분자를 붙여주는 방법 (Bean 이름을 변경하는 것은 아님)
  • 주입 시에 @Qualifier를 붙여주고 이름 등록
  • @Qualifier 이름을 통해 찾지 못할 경우 해당 이름의 스프링 빈을 추가로 찾음 (예를 들자면, @Qualifier("mainRepository")를 못 찾을 경우 MainRepository라는 스프링 빈을 찾음)
  • 그럼에도 찾지 못할 경우 NoSuchBeanDefinitionException 예외 발생


 

@Primary 사용

  • 우선순위를 정하는 방법
  • @Autowired 어노테이션을 통해 여러 빈을 찾을 경우 @Primary 어노테이션이 추가된 빈이 우선권을 가짐


 

@Qualifier vs @Primary

  • @Qualifier 어노테이션의 경우 사용하기 위해서는 모든 코드에 @Qualifier를 추가해줘야 하는 단점 존재
  • @Primary 어노테이션의 경우 그러한 단점 X
  • 우선순위는 @Qualifier가 @Primary 보다 높음

 

@Qualifier, @Primary 활용 예시

  • 위 예시 코드처럼 애플리케이션에서 메인으로 사용하는 데이터베이스의 커넥션을 획득하는 스프링 빈(JpaRepository)이 있고, 테스트용으로 사용하는 데이터베이스의 커넥션을 획득하는 스프링 빈(MyBatisRepository)이 있다고 가정
  • JpaRepository를 주로 사용하므로 @Primary 어노테이션을 적용해 조회하는 곳에서 @Qualifier 어노테이션 없이 편리하게 사용
  • MyBatisRepository는 가끔 사용하므로 @Qualifier를 지정하여 명시적으로 획득하며 사용

 

참고

인프런 스프링 핵심 원리 - 기본편 (김영한 강사님)

반응형