Spring boot

[기본] 의존관계 자동 주입

메모

 

ctrl + F12: 메소드 보기

 

ctrl + alt + B : 구현체 보기

 

ctrl + alt + insert : new

 

ctrl + B : 해당 클래스 사용하는 파일 추적

 

ctrl + N : Navigate to class

 


다양한 의존관계 주입 방법

 

의존관계 주입 방법은 크게 4가지이다.

  1. 생성자 주입 - 주로 불변/필수 의존관계에 사용한다. 생성자가 1개인경우 @Autowired 생략 가능 
  2. 수정자 주입(Setter 주입) - setter 메서드를 통해 DI한다. 선택/변경 가능성 있을 때 사용
  3. 필드 주입 - 비추 외부 변경이 불가능해 순수 자바코드로 테스트 불가능하다.
  4. 일반 메서드 주입

 

참고: 스프링 컨테이너가 관리하는 빈이어야 동작한다.

 

실무에서는 자동/수동 등록 모두 사용한다.

 

final키워드 : 필수적으로 값이 있어야 한다. 생성자에서 의존관계 연결시켜주는 것을 누락했을 경우 테스트 짤 때 까지는 문제 모름 final 키워드를 사용하면 누락시킬 일 없음

 

setXxxxx~~ : 자바빈 프로퍼티 규약, 수정자 이와 같이 사용함

수정자에 @Autowired 빼먹으면 의존관계 주입 안된다.

 

스프링은 빈을 생성하는 단계와 의존관계 주입하는 단계로 나눠져 있음

생성자 주입만 빈을 등록하는 단계에서 의존관계도 같이 주입함

 

수정자는 필수값이 아닐때도 사용할 수 있음 (required = false)

 

필드주입은 스프링컨테이너를 통해서가 아닌 순수자바로 테스트 할 수 없음 (외부에서 변경x) -> 쓰지말자 테스트에서는 써도 됨

 

스프링 없이 순수한 자바코드로 테스트할 일 많음

 


옵션 처리

 

자동 주입 대상을 옵션으로 처리하는 방법은 다음과 같이 3가지이다.

  1. @Autowired(required = false) // 호출 안됨
  2. @Autowired
    public void setNoBean(@Nullable Member member) {} // null호출
  3. @Autowired
    public void setNoBean(Optional<Member> member) {} //Optional.empty 호출

 


생성자 주입을 선택해라!

 

불변

대부분의 애플리케이션은 의존관계를 변경하면 안된다.

수정자 주입은 setXxx메서드를 public으로 두어 변경 가능성이 있다.

생성자 주입은 객체 생성 시 딱 1번만 호출되므로 안전하다.

 

누락

수정자 주입으로 순수 자바코드 단위 테스트 진행 시 의존관계를 누락할 수 있다.

생성자 주입을 사용하면 데이터 누락시 컴파일 오류가 발생한다. (컴파일 오류가 가장 좋다)

 

final 키워드

생성자 주입 시 필드에 final 키워드를 사용하므로써 값이 설정되지 않는 오류를 컴파일 시점에 막아준다.

(생성자 주입을 제외한 방법은 final 키워드를 사용할 수 없다)

 

 


롬복과 최신 트렌드

 

생성자 주입을 필드 주입처럼 간편히 할 수 없을까 ? -> 롬복을 사용하자

 

@Component
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

 

Lombok의 @RequiredArgsConstructor 기능을 통해 final이 붙은 필드를 모아 생성자를 자동으로 만들어줬다.

 

롬복이 적용된 생성자

참고:

Enable Annotation Processing 해줘야 Lombok을 사용할 수 있다.

 

이 밖에도 실무에서 정말 많이 사용하는 롬복 기능

@Getter

@Setter

@ToString 등이 있다.

 


조회 빈이 2개 이상 - 문제, 해결방법

 

@Autowired는 타입으로 빈을 조회하기 때문에 같은 타입의 빈이 2개 이상일 때 NoUniqueBeanDefinitionException오류가 발생한다.

하위 타입으로 지정하는 것은 DIP를 위배하고 유연성을 떨어뜨린다.

 

해결 방법은 다음과 같다.

  1. @Autowired 필드명 매칭하기 - 타입 매칭 시도 후 파라미터와 필드 이름으로 빈 이름을 추가 매칭한다.
  2. @Qualifier 로 빈 이름을 매칭하기 - 추가 구분자를 붙이고 구분자로 매칭한다. 만약 구분자를 찾지 못할 경우 해당 구분자 이름의 스프링 빈을 추가로 찾는다. 그치만 추가 기능 사용은 권장하지 않음
  3. @Primary로 우선순위 지정하기 - @Primary 가 붙은 빈이 우선권을 가진다. 자주 사용됨

 

primary, qualifier 활용

@Primary는 메인으로 사용하는 빈에 적용하고 서브로 사용하는 빈은 @Qualifier를 지정하여 명시적으로 획득하면 깔끔하다.

 

스프링은 항상 다음과 같이 우선순위를 가져간다.

수동 > 자동

좁은 선택 범위 > 넓은 선택 범위

 

따라서 primary 와 qualifier가 둘 다 사용되었을 때에는 명시적인 @Qualifier가 우선순위를 가져간다.

 


애노테이션 직접 만들기

@Qualifier("mainDiscountPolicy") 이렇게 적으면 컴파일시 타입 체크가 안된다.

Qualifier 자체 애노테이션과 @Qualifier("mainDiscountPolicy")를 추가한 새로운 커스텀 애노테이션을 만들어 사용하면 잘못 타입했을 때 컴파일 오류를 생성할 수 있다.

 

애노테이션에는 상속 개념이 없다.

여러 애노테이션을 모아 사용하는 것은 스프링의 기능이다.

다른 애노테이션도 함께 조합하여 사용할 수 있지만 스프링 기능을 뚜렷한 목적 없이 무분별하게 재정의하는 것은 유지보수 측면에서 권장하지 않는다.

 


조회한 빈이 모두 필요할 때, List/Map

 

클라이언트가 할인 종류를 선택할 때처럼 특정 타입의 스프링 빈이 모두 필요한 경우가 있다.

private final Map<String, DiscountPolicy> policyMap;
private final List<DiscountPolicy> policies;

DiscountService(Map<String, DiscountPolicy> policyMap, List<DiscountPolicy> policies) {
	this.policyMap = policyMap;
	this.policies = policies;
	System.out.println("policyMap = " + policyMap);
	System.out.println("policies = " + policies);
}

결과

map의 키에 스프링 빈의 이름, 값에 타입으로 조회한 빈을 담아준다.

List는 타입으로 조회한 모든 스프링 빈 인스턴스를 담아준다.

 


자동, 수동의 올바른 실무 운영 기준

 

설정 정보 구성 부분과 동작 부분을 명확히 나누는 것이 이상적이지만, 빈이 많아질수록 작업이 번거로워지므로 자동 기능을 기본으로 사용하는 것이 좋다.

또한 자동 빈 등록은 어노테이션을 조금 수정할 순 있지만 그것만 제외하면 OCP, DIP를 지킨다.

 

컨트롤러, 서비스, 리포지토리 등의 업무 로직 빈은 주로 자동 기능을 사용해도 어떤 곳에서 문제가 발생했는지 파악하기 쉽다.

그러나 AOP 등의 기술 지원 빈수동 빈 등록을 사용해 명확하게 드러내는 편이 유지보수 측면에서 좋다. 수가 매우 적고, 문제가 발생했을 때 파악하기 어렵기 때문.

 

비즈니스 로직 중 다형성을 적극 활용할 때에는 수동 빈으로 등록하거나 자동 기능을 사용하되 특정 패키지에 같이 묶어두어야 편리하다.

 

참고: 스프링, 스프링 부트가 자동으로 등록하는 수많은 빈들은 예외이다. 스프링 자체를 잘 이해하고 스프링의 의도대로 사용하자.

'Spring boot' 카테고리의 다른 글

[기본] 빈 스코프  (0) 2021.08.24
[기본] 빈 생명주기 콜백  (0) 2021.08.24
[기본] 컴포넌트 스캔  (0) 2021.08.16
[기본] 싱글톤 컨테이너  (0) 2021.08.12
[기본] 스프링 컨테이너와 스프링 빈  (1) 2021.08.11