본문 바로가기
Spring

자바 스프링 - 의존 자동 주입 정리

by onggury 2023. 3. 29.

의존 자동 주입이란?

스프링 DI를 보면 객체를 직접 생성자, 객체 주입 방식으로 의존 주입을 했습니다.

이를 자동으로 해줄 수 있는데 @Autowired 어노테이션을 활용하면 의존을 자동으로 주입할 수 있습니다.


 

@Autowired 어노테이션을 메서드나 필드에 붙임으로써 의존을 자동 주입해 달라는 선언을 하게 됩니다.

 

public class MemberRegisterService {

	@Autowired
	private MemberDao memberDao;
	
	public MemberRegisterService() {
	}
	
	/*
	public MemberRegisterService(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
	*/ // 의존 자동 주입을 함으로써 생성자 주입 방식은 사용할 필요가 없어졌다.
    		.
            	.
                .
}

 

예를 들어, 위의 코드는 MemberDao 객체 타입에 의존을 자동으로 주입하려고 필드에 선언했습니다.

 

그러나 그냥 무작정 @Autowired 어노테이션만 붙이면 되는 것이 아닙니다.

 

스프링 컨테이너에 등록된 Bean 객체를 찾아서 자동으로 주입해 주는 방식이기 때문에

우리는 설정 클래스를 살펴볼 필요가 있습니다.

 

 

@Configuration
public class AppContext {

	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
    		.
            	.
                .
}

 

지난번 DI 에 대한 정리 글에 @Configuration도 소개해 줬죠. 해당 어노테이션을 붙임으로써 AppContext 클래스는 설정 클래스가 됩니다.

그리고 memberDao는 @Bean 어노테이션을 붙여서 스프링 컨테이너에 Bean 클래스로 등록을 했습니다.

 

@Autowired는 Bean 객체를 타입 -> 필드/메서드 명 순서로 찾아서 자동 주입을 합니다.

 

아래 예시를 보며 얘기하자면,

@Configuration
public class AppContext {

	@Bean
	public MemberDao memberDao1() {
		return new MemberDao();
	}
    
    	@Bean
	public MemberDao memberDao2() {
		return new MemberDao();
	}
    		.
            	.
                .
}

MemberDao 타입 Bean 객체가 두 개가 있습니다.

 

1. MemberDao 타입의 Bean 객체를 찾는다.

2. 만약 해당 타입이 두 개 이상 존재하면 필드/메서드 명으로 검색한다.

3. 그래도 해당 Bean 클래스를 찾지 못하면 NoUniqueBeanDepenencyException 예외가 발생한다.

 

물론 저렇게 클래스 타입이 두 개 이상 존재한다 해도, @Autowired가 붙은 필드/메서드의 이름과 Bean 클래스의 이름이 같다면 자동 주입이 됩니다.

 


 

그런데, 설정 클래스는 반드시 하나여야 하나요?

 

아뇨. 하나만 존재 할 필요가 없습니다.

 

생성하고 관리해야 할 Bean의 수가 증가한다면, 영역별로 설정 클래스를 분리해서 관리할 수 있습니다.

 

 

예를 들어 아래 코드와 같습니다.

 

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import chap03.model.*;  // chap03.model 패키지 안에 있는 모든 클래스 import
// MemberDao, MemberPrinter, VersionPrinter를 불러오기 위함.

@Configuration
public class AppConf1 {  // 설정 클래스

	@Bean
	public MemberDao memberDao() {  // Member의 정보 값을 처리하는 클래스
		return new MemberDao();
	}
	
	@Bean
	public MemberPrinter memberPrinter() {  // Member 정보를 출력하는 클래스
		return new MemberPrinter();
	}
}

 

AppConf1 클래스는 @Configuration 어노테이션이 붙은 설정 클래스 입니다.

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import chap03.model.*;

@Configuration
public class AppConf2 {  // 설정 클래스

	@Autowired
	private MemberDao memberDao;
	
	@Autowired
	private MemberPrinter memberPrinter;
	
	@Bean
	public MemberRegisterService memberRegSvc() {  // 생성자 주입 방식
		return new MemberRegisterService(this.memberDao);
	} // Member의 정보를 입력받아 등록 요청을 처리하는 클래스
        /*
   	 MemberRegisterService 클래스는 Member 정보를 불러오기만 하고
    	실질적인 값의 처리는 MemberDao가 하기 때문에 MemberRegisterService 클래스의 생성자로 의존 주입을 합니다.
    	*/
	
	@Bean
	public ChangePasswordService changePwdSvc() { // 객체 주입 방식
		ChangePasswordService pwdSvc = new  ChangePasswordService();
		pwdSvc.setMemberDao(this.memberDao);
		
		return pwdSvc;
	}  // Member 정보의 비밀번호를 변경하는 클래스
    	/*
    	ChangePasswordService클래스는 Member 정보를 불러오기만 하고
    	실질적인 값의 처리는 MemberDao가 하기 때문에 ChangePasswordService클래스의 setter메서드로 의존 주입을 합니다.
    	*/
    
    	@Bean
	public MemberListPrinter listPrinter() {
		return new MemberListPrinter(this.memberDao, this.memberPrinter);
	}
}

Appconfi1 클래스는 Member 정보의 값을 처리하는 역할을 수행합니다.

Appconfi2 클래스는 사용자가 등록 요청을 하거나, 비밀번호 변경 요청을 하거나, Member 전체 정보를 출력해 달라는 요청을 받는 클래스입니다. (해당 예시는 실질적인 값의 처리는 MemberDao 클래스가 처리하는 단순한 예시입니다.) 

 

이렇게 역할을 나눠주게 되면서 유지보수 또한 한 층 더 수월해지겠죠.

 


 

@Qualifier 어노테이션으로 Bean에게 별칭(Alias) 지정

@Qualifier("별칭")을 사용하시면 해당 별칭으로 Bean 클래스를 탐색합니다.

 

이는 필드/메서드 명 보다 우선 탐색됩니다.

즉, 타입 -> 별칭 -> 이름 순서로 탐색하는 것입니다.

 

 

아래 예시를 보며 사용 법을 보겠습니다.

 

@Configuration
public class AppContext {

	@Bean
    	public MemberPrinter memberPrinter1() {
    		return new MemberPrinter();
    	}
    
	@Bean
	@Qualifier("memberPnt")
	public MemberPrinter memberPrinter2() {
		return new MemberPrinter();
	}
}
//////////////////////////////////////////////////////////////////////////////////////////

public class MemberInfoPrinter {

	@Autowired
    	private MemberPrinter printer1;

	@Autowired
    	@Qualifier("memberPnt")
    	private MemberPrinter printer2;
    
}

(하나의 파일 안에 저렇게 적는 경우는 없지만, 이해를 돕기 위해 한 곳에 적었습니다.)

 

해당 클래스는 @Configuration 어노테이션이 붙었으니 설정 클래스입니다.

그리고 MemberInfoPrinter 클래스는 @Autowired로 의존 자동 주입을 하고 있죠.

 

@Bean 어노테이션과 @Qualifier 어노테이션이 함께 사용된 것을 볼 수 있습니다.

이처럼 @Qualifier 어노테이션은 @Bean, @Autowired 어노테이션과 함께 사용됩니다.

 

@Bean 어노테이션과 @Qualifier 어노테이션을 함께 사용하면, 해당 별칭으로 Bean 클래스 등록.

@Autowired 어노테이션과 @Qualifier 어노테이션을 함께 사용하면, @Qualifier 어노테이션으로 지정한 값(별칭)의 Bean 객체를 찾아서 의존 자동 주입을 합니다.

 

해당 그림처럼 의존 자동 주입이 이뤄집니다.

 

 

단, 필드명을 별칭으로 사용 시, 오류가 발생합니다.

필드 명은 별칭으로 지정할 수 없고, 오로지 빈의 이름만 지정할 수 있습니다.