본문 바로가기
서버/스프링

[Spring] 의존관계 자동 주입 @Autowired

by 베어 그릴스 2022. 8. 27.
320x100

*본 게시글은 김영한님 스프링 핵심 원리 기본편을 보고 이해한 내용을 바탕으로 정리한 글입니다.

 

 

의존관계 자동 주입

스프링에서는 객체 간의 의존관계를 자동으로 주입해주는 @Autowired라는 어노테이션을 제공한다.

 

*의존관계 주입(DI란?)

 

[Spring] Ioc(제어의 역전)와 DI(의존성 주입)의 개념과 그 차이

이번에는 스프링 공부를 하는 사람들에겐 필수적이자만 많이들 헷갈려하는 개념인 Ioc ( Inversion of Control ) 와 DI ( Dependency Injection )의 개념에 대하여 알아보겠습니다. Ioc - Inversion of Control 우..

developbear.tistory.com

 

이를 통해 사용자가 외부에서 직접 의존관계를 주입해주지 않아도, 스프링에서 자동으로 의존관계를 주입해준다.

 

 

의존관계를 주입하는 방법은 총 4가지가 있다.

  • 생성자 주입
  • 수정자 주입(setter 주입)
  • 필드 주입
  • 일반 메서드 주입

4가지 방법에 대하여 알아보자.

 

생성자 주입

이름 그대로 생성자를 통해서 의존관계를 주입 받는 방법이다.

가장 흔히 쓰이는 방법이다.

@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;
    }
}

위와 같이 생성자를 통해서 의존 관계를 주입 받기를 원하는 경우에 쓰인다.

자동으로 스프링 컨테이너에서 getBean(MemberRepository.class)을 통해 스프링 빈을 가져와서 의존관계를 주입해준다.

 

만일, 생성자가 하나만 있다면 @Autowired를 생략해도 자동으로 의존관계가 주입된다.

 

생성자는 호출 시점에 딱 1번만 호출되기 때문에 한번 의존관계를 주입받으면 해당 객체는 불변한다.

 

또 객체가 불변하기 때문에 final 키워드를 통해서 객체의 값이 설정되지 않는 오류를 컴파일 시점에서 막아준다.

 

위 특징 때문에 보통 생성자 주입을 많이 쓴다.

 

*참고 : 롬복 라이브러리가 제공하는 @RequiredArgsConstructor 기능을 사용하면 final이 붙은 필드를 모아서 생성자를 자동으로 만들어주는데, 생성자가 그것 하나라면 @Autowired도 자동으로 그 위에 붙게되고 코드가 매우 간결해진다.

 

수정자 주입 (setter)

setter라 불리는 필드의 값을 변경하는 수정자 메서드를 통해서 의존관계를 주입하는 방법이다.

자바빈 프로퍼티 규약의 수정자 메서드 방식을 사용하는 방법이다.

@Component
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;
	}
}

setter는 객체의 변화의 여지를 남겨두는 것이기 때문에 수정자 주입은 선택,변경 가능성이 있는 의존관계에 사용한다.

 

 

필드 주입

이름 그대로 필드에 바로 주입하는 방법이다.

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

코드가 간결해서 좋지만, DI 프레임워크 없이는 아무것도 못하기 때문에 잘 사용되지 않는다.

 

 

일반 메서드 주입

일반 메서드를 통해서 주입 받는 방법이다.

setter가 아닌 다른 일반 메서드를 사용하는 것이다.

잘 사용되지 않는다.

 

 

등록되어있지 않은 스프링 빈을 주입받을 때

기본적으로 오류가 난다.

 

등록되어있지 않은 스프링빈을 주입 받아도 오류가 나지 않게끔, 즉, Null값이 와도 되게끔 허용하는 방법에 대하여 알아보자.

 

1. @Autowired(required = false)

 

자동 주입할 대상이 없으면 수정자 메서드 자체가 호출이 안된다.

 

 

2. null이 와도 되는 매개 변수 앞에 @Nullable 어노테이션

 

자동 주입할 대상이 없으면 null이 입력된다.

 

 

3.null이 와도 되는 매개 변수 타입을 Optional<>

 

자동 주입할 대상이 없으면 Optional.empty 가 입력된다.

 

 

조회된 빈이 2개 이상일 때

자동 주입 할 빈을 찾을 때 클래스 타입으로 조회한다.

 

OCP 원칙을 위해 인터페이스를 통해 의존성 주입을 받고, 그 인터페이스를 상속받은 스프링 빈이 두개 이상일 때는 조회되는 빈이 2개이기 때문에 오류가난다.

 

그렇다면 의존성 주입을 받을 때 생성자의 타입을 더 하위 타입으로 지정해야하는 것일까?

 

이는 DIP를 위해하고 유연성이 떨어진다.

 

이를 해결하는 방법 3가지를 알아보자.

 

1. @Autowired 필드 명 매칭

@Autowired 는 타입 매칭을 시도하고, 이때 여러 빈이 있으면 필드 이름, 파라미터 이름으로 빈 이름을 추가 매칭한다.

 

2. @Qualifier

@Qualifier("이름") 을 빈 등록 시 빈에 붙여주고, 주입 시에 파라미터 앞에 똑같이 @Qualifier("이름")을 붙여준다.

 

그러면 @Qualifier끼리 우선 매칭하고 이후에 빈 이름 매칭을 하고 그래도 없으면 오류를 낸다.

 

3.@Primary

@Primary을 빈 등록 시 빈에 붙여주면 해당 스프링 빈에 우선권을 두어 의존성 주입을 한다.

 

@Qualifier와 @Primary가 둘다 있으면, @Qualifier가 더 우선권이 높다.

훨씬 상세히 이름까지 정해주니까 보다 이런 경우 보다 구체적인 쪽이 더 우선권을 갖는다.

 

예를 들어서, 자주 사용하는 메인 데이터베이스 커넥션을 획득하는 스프링 빈이 있고, 코드에서 특별한 기능으로 가끔 사용하는 서브 데이터베이스의 커넥션을 획득하는 스프링 빈이 있다고 생각해보자.

 

이 때는 메인 데이터베이스의 커넥션을 휙득하는 스프링 빈은 @Primary를 적용해서 코드를 간단히 하고, 서브 데이터베이스 커넥션을 휙들할 때는 @Qualifier를 사용해서 명시적으로 휙득하는 방식으로 사용하면 코드가 깔끔해진다.

 

 한 타입의 스프링 빈이 다 모두 의존관계 주입이 필요할 때

 

의도적으로 정말 해당 타입의 스프링 빈이 다 필요한 경우도 있다.

 

이럴 때는 의존 관계 주입을 받는 변수의 타입을 Map<String, 해당 타입> 변수명 혹은 List<해당 타입> 변수명으로 하면 해당 타입으로 빈을 조회한 것이 두개 이상 나와도 해당 자료구조에 빈들이 모두 들어가게 된다.

 

 

 

 

 

728x90