본문 바로가기
Spring/Spring의 IoC

Bean의 의존성 주입

by 융디's 2024. 6. 16.
728x90
Bean의 의존성 주입

Bean의 의존성 주입

의존성 주입(Dependency Injection, DI)은 객체가 직접 필요한 의존 객체를 생성/관리하는 것이 아닌, 외부에서 의존 객체를 주입받아 사용하는 디자인 패턴이다. Spring Framework에서는 주로 세 가지 방식으로 의존성을 주입한다.

이 세 가지 방식은 모두 Ioc(Inversion of Control) 컨테이너에 의해 관리된다.

자동 주입

div style="width:100%">Bean 객체가 생성되면 DI 컨테이너해당 빈에 자동으로 필요한 의존성을 주입

< 어떻게 자동으로 주입하는데 >

  1. 생성자 주입 방식
  1. setter 주입 방식
  1. 필드 주입 방식(@Autowired)

< 의존성 주입 순서 >

  • 생성자 → 필드 → setter

생성자 기반 의존성 주입 방식

객체를 최소 생성 시점에 필요한 의존 객체를 생성자 파라미터로 전달받아 객체를 초기화하는 방식
  • 공식적으로 Spring은 생성자 주입 방식을 추천한다.
    • 생성자에 주입된 컴포넌트들이 완전히 초기화된 상태로 클라이언트에게 반환되기 때문
    • 따라서 필드 주입이나 setter 주입과 달리 NullPointerException을 방지할 수 있다.
  • 생성자가 1개밖에 없을 경우 @Autowired를 생략해도 무방
  • 일반적으로 의존성이 반드시 필요하는 경우에 사용
  • 필드를 final로 만들 수 있다.
@Controller
public class MemberController{
	private final MemberService memberService;
	
	public MemberController(MemberService memberService){
			this.memberService = memberService;
	}
}

만약 생성자가 여러 개라면?

  • 의존성을 자동으로 주입하는 데 사용할 생성자에 @Autowired 붙이기
  • @Autowired 가 여러 개 있을 경우 가장 많은 의존성을 주입할 수 있는 생성자를 사용
  • @Autowired 가 붙은 모든 생성자가 사용 불가능한 경우 또는 어떤 생성자에도 @Autowired 가 없을 경우는 기본 생성자를 호출
  • 만약 기본 생성자조차 없다면 컴파일 에러 발생

만약 주입 대상이 여러 개라면?

MemberService 인터페이스가 존재하고, 이를 구현하는 삼성과 LG 구현클래스가 모두 빈으로 등록되어 있을 때, MemberController에서는 MemberService 인터페이스 타입의 필드가 존재하고, 이를 생성자 주입 방식을 통해 의존성이 주입된다. 이때 해당 인터페이스 필드는 어떤 구현 클래스를 주입 받게 될까?

< 예시 코드 >

@Service("samsungService")
public class SamsungMemberService implements MemberService {}

@Service("lgService")
public class LGMemberService implements MemberService {}
@Controller
public class MemberController {

    private final MemberService memberService;

    @Autowired
	   // 삼성을 받아야돼? 아니면 LG를 받아야돼? 
    public MemberController(MemberService memberService) {
        this.memberService = memberService;
    }
}

  • 만약 그냥 실행 시 에러 발생
    • MemberController에서는 딱 하나의 MemberService만 필요하기 때문
  • 스프링은 의존성 대입 대상을 찾을 때 정의되어 있는 타입을 기준으로 찾는다.
  • 타입을 기준으로 여러 bean이 검색되었다면 그다음으로 bean의 이름을 기준으로 찾는다
  • 방법 1. @Qualifier(”빈 구분자 명”)를 통해 bean의 구분자를 지정해 준다.
    @Controller
    public class MemberController {
    
        private final MemberService memberService;
    
        @Autowired
        public MemberController(@Qualifier("samsungService") MemberService memberService) {
            this.memberService = memberService;
        }
    }
    
  • 방법 2. @Primary를 통해 해당 빈이 해당 타입의 기본 빈으로 설정
    @Primary
    @Service("samsungService")
    public class SamsungMemberService implements MemberService {}
    
    @Service("lgService")
    public class LGMemberService implements MemberService {}
  • 방법 1과 2가 동시에 있을 @Qualifier가 우선순위를 갖는다.

< 의존성 주입 기준 >

  • 타입 → @Qualifier → @Primary → 변수 명

설정자 기반 의존성 주입 방식

객체를 생성한 후, 필요한 의존 객체를 설정자(setter) 메서드를 통해 주입하는 방식
  • 빈 객체를 만들고 setter로 의존성을 주입하므로 기본 생성자가 필요하다!
    • final 필드를 만들 수 없고, 의존성의 불변을 보장할 수 없다.
@Controller
public class MemberController{
	private MemberService memberService;
	
	@Autowired
	public void setMemberService(Memberserivce memberService){
			this.memberService = memberService;
	}
}

필드 기반 의존성 주입 방식

@Autowired을 이용하여 객체의 필드에 직접 의존 객체를 주입하는 방식(지양하는 방식)
  • 스프링 컨테이너가 객체를 생성한 후, 의존성을 클래스의 프라이빗 필드에 직접 주입한다.
  • 생성자나 setter()가 필요 없어지기 때문에 코드가 간결해지고, 의존성을 주입하는 과정이 자동화되므로 생산성이 향상
  • 클래스가 스프링 컨테이너에 너무 강하게 의존하게 된다.
    • 테스트 등의 이유로 수동 의존성 주입을 하고 싶어도 생성자도, setter도 없으므로, 불가능
  • final 키워드를 사용할 수 없어 객체의 불변성을 보장 못 한다.
@Controller
public class MemberController{
	@Autowired
	private MemberService memberService;
}

< 필드 기반 의존성 주입 과정 >

  1. 스캔 및 빈 등록
    • Spring 컨테이너는 @Component, @Service, @Repository, @Controller 등이 붙은 클래스를 스캔하여 빈으로 등록
    • 이 과정에서 클래스의 메타데이터를 분석한다.
  1. 의존성 주입 지점 탐색
    • 등록된 빈을 분석하면서, @Autowired가 붙은 필드를 찾는다.
  1. 적절한 빈 주입
    • 해당 필드 타입에 맞는 빈을 검색한다.
    • 해당 타입의 빈이 여러 개 있을 경우, @Qualifier나 @Primary, 필드 이름을 통해 적절한 빈을 주입한다.

728x90

'Spring > Spring의 IoC' 카테고리의 다른 글

Bean  (0) 2024.06.15
IoC와 DI  (0) 2024.06.12