본문 바로가기

Java/Spring

Spring 5 입문: Chapter 04. 자동 의존 주입

더보기

이 프로젝트의 개발 환경

  • 개발 언어 및 개발 환경
    • OpenJDK 17
  • 기타 환경
    • macOS Sonoma 14.1.1
    • IntelliJ IDEA 2020.3 Ultimate Edition

이전 예제에서는 스프링의 의존 주입(DI, Dependency Injection)에 대해서 살펴봤습니다.

설정 클래스 AppContext는 주입할 의존 대상을 생성자 또는 메서드를 사용하여 DI 했습니다.

@Configuration
public class AppConttext {
	@Bean
    public MemberDao memberDao() {
    	return new MemberDao();
    }
    
    @Bean
    public ChangePasswordService changePwdSvc() {
    	ChangePasswordService pwdSvc = new ChangePasswordService();
        pwdSvc.setMemberDao(memberDao());
        return pwdSvc;
    }
}

자동 주입은 설정 클래스에서 DI 코드를 직접 작성하지 않고 스프링의 기능을 사용합니다.

예제 프로젝트 생성

sp5-chap04 프로젝트를 생성하고 chap04 패키지를 추가합니다.

이전 프로젝트의 chap03 패키지의 소스 파일을 복사하여 사용합니다.

 tree    
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    └── main
        ├── java
        │   ├── AppContext1.java
        │   ├── AppContext2.java
        │   ├── ChangePasswordService.java
        │   ├── Main.java
        │   ├── Member.java
        │   ├── MemberDao.java
        │   ├── MemberListPrinter.java
        │   ├── MemberPrinter.java
        │   ├── MemberRegisterService.java
        │   └── RegisterRequest.java
        └── resources

자동 주입을 위한 @Autowire

자동 주입은 주입 대상에 @Autowire 어노테이션을 등록합니다.

더보기

자동 주입은 @Autowire @Resource @Inject 어노테이션을 사용합니다.

세 가지 어노테이션이 모두 유효하지만 이 예제에서는 @Autowire을 사용합니다.

ChangePasswordService 클래스의 DI 코드를 다음과 같이 수정합니다.

import org.springframework.beans.factory.annotation.Autowired;

public class ChangePasswordService
{
	@Autowired
	private MemberDao memberDao;
	
	public void changePassword(String email,
	                           String oldPassword,
	                           String newPassword)
	{
		Member member = memberDao.selectByEmail(email);
		if (null == member)
			throw new RuntimeException();
		member.changePassword(oldPassword, newPassword);
		memberDao.update(member);
	}
	
	@Deprecated
	public void setMemberDao(MemberDao memberDao)
	{
		this.memberDao = memberDao;
	}
}
코드 비고
Line 5:6 @Autowired MemberDao 필드에 @Autowired 어노테이션을 등록했습니다.
이 어노테이션은 설정 클래스에서 DI 하지 않더라도 자동 주입됩니다.
Line 19:23 @Depreced MemberDao 필드는 자동 주입되므로 이 함수는 더 이상 사용하지 않습니다.

AppContext 클래스의 DI 코드는 다음과 같이 수정합니다.

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

@Configuration
public class AppContext
{
	@Bean
	public ChangePasswordService changePwdSvc()
	{
		ChangePasswordService svc = new ChangePasswordService();
		// svc.setMemberDao(memberDao);
		return svc;
	}
}
코드 비고
Line 11 setMemberDao() ChangePasswordService 클래스에서 MemberDao는 자동 주입되므로 DI 하지 않습니다.

@Autowired 어노테이션은 자동 주입하려는 필드 뿐만 아닐라 메소드에서도 사용 할 수 있습니다.

테스트를 위해서 MemberInfoPrinter 클래스를 생성합니다.

import org.springframework.beans.factory.annotation.Autowired;

public class MemberInfoPrinter
{
	private MemberDao memberDao;
	private MemberPrinter memberPrinter;
	
	public void printMemberInfo(String email)
	{
		Member member = memberDao.selectByEmail(email);
		if (null == member)
			throw new NullPointerException();
		memberPrinter.print(member);
		System.out.println();
	}
	
	@Autowired
	public void setMemberDao(MemberDao memberDao)
	{
		this.memberDao = memberDao;
	}
	
	@Autowired
	public void setPrinter(MemberPrinter printer)
	{
		this.memberPrinter = printer;
	}
}
코드 비고
Line 17 Line 23 @Autowired 두 Setter에서 자동 주입을 실행합니다.

MemberInfoPrinter를 Bean으로 지정합니다.

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

@Configuration
public class AppContext
{
	@Bean
	public MemberInfoPrinter infoPrinter() 
	{
		return new MemberInfoPrinter(); 
	}
}
코드 비고
Line 7:11 @Bean MemberInfoPrinter를 Bean으로 지정하였으나 클래스가 요구하는 필드는 DI로 처리하지 않습니다.

테스트 코드를 실행하기 위해 Main 클래스를 다음과 같이 수정합니다.

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main
{
	private static ApplicationContext ctx = null;
	
	public static void main(String[] args) throws IOException
	{
		ctx = new AnnotationConfigApplicationContext(AppContext.class);
		BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
		
		while (true) {
			(... 중략 ...)
			else if (command.startsWith("info"))
			{
				processInfoCommand(command.split("[ ]+"));
				continue;
			}
			else
			{
				printHelp();
			}
		}
	}
	
	private static void processInfoCommand(String[] args)
	{
		if (2 != args.length)
		{
			printHelp();
			return;
		}
		
		MemberInfoPrinter infoPrinter = ctx.getBean("infoPrinter", MemberInfoPrinter.class);
		infoPrinter.printMemberInfo(args[1]);
	}
}
코드 비고
Line 19:23 command.startWith("info") 콘솔에서 info로 시작하는 명령줄을 추가합니다.
Line 31:41 processInfoCommand() MemberInfoPrinter를 Bean 객체로 접근하고 입력된 이메일에 따른 멤버 정보를 표시합니다.
더보기

애플리케이션을 실행하고 새로 추가한 info 명령줄의 정상 동작을 확인합니다.

예제에서는 AppContext에서 MemberInfoPrinte를 Bean으로 등록하고 Bean 객체의 필드를 자동 주입합니다.

스프링은 Bean 객체의 Setter 메소드에 @Autowired 어노테이션을 등록하면 Bean 객체가 생성되는 시점에 해당 메소드를 호출합니다.

그리고 @Autowired 메소드의 파라미터 타입에 해당하는 Bean 객체를 자동으로 중비합니다.

나머지 다른 Bean 객체에서도 DI 코드를 자동 주입으로 수정합니다.

MemberRegisterService 클래스에서는 MemberDao 필드에 자동 주입을 실행하고 기본 생성자를 추가합니다.

import org.springframework.beans.factory.annotation.Autowired;

import java.time.LocalDateTime;

public class MemberRegisterService
{
	@Autowired
	private MemberDao memberDao;
	
	public MemberRegisterService()
	{
	
	}
	
	public MemberRegisterService(MemberDao memberDao)
	{
		this.memberDao = memberDao;
	}
}
코드 비고
Line 7:8 @Autowired MemberDao 필드를 자동 주입합니다.
Line 10:13 MemberRegisterService() 기본 생성자를 새로 추가합니다.
더보기

@Autowired는 Setter 메소드 뿐만 아니라 Bean 객체의 필드에서도 동작합니다.

스프링은 Bean 객체의 필드에서 자동 주입이 필요한 경우 필드 타입과 일치하는 Bean 객체를 설정 파일에서 찾고 자동 주입합니다.

MemberListPrinter 클래스는 다음과 같이 수정합니다.

import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;

public class MemberListPrinter
{
	private MemberDao memberDao;
	private MemberPrinter memberPrinter;
	
	public MemberListPrinter()
	{
	}
	
	@Autowired
	public void setMemberDao(MemberDao memberDao)
	{
		this.memberDao = memberDao;
	}
	
	@Autowired
	public void setMemberPrinter(MemberPrinter memberPrinter)
	{
		this.memberPrinter = memberPrinter;
	}
}
코드 비고
Line 10:12 MemberListPrinter() 기존 생성자를 삭제하고 기본 생성자를 새로 추가합니다.
Line 14:24 Setter 자동 주입을 위한 Setter를 추가합니다.

기존 DI 코드를 모두 자동 주입으로 수정했으므로 AppContext에서 더 이상 DI를 처리하지 않습니다.

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

@Configuration
public class AppContext
{
	@Bean
	public MemberDao memberDao()
	{
		return new MemberDao();
	}
	
	@Bean
	public MemberPrinter memberPrinter()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberRegisterService memberRegSvc()
	{
		return new MemberRegisterService();
	}
	
	@Bean
	public ChangePasswordService changePwdSvc()
	{
		return new ChangePasswordService();
	}
	
	@Bean
	public MemberListPrinter listPrinter()
	{
		return new MemberListPrinter();
	}
	
	@Bean
	public MemberInfoPrinter infoPrinter()
	{
		return new MemberInfoPrinter();
	}
}

일치하는 Bean이 없는 경우

자동 주입은 필드의 타입 또는 Setter 메소드의 인자 타입에 의존합니다.

만약 자동 주입을 위한 Bean이 설정 클래스에 등록되어 있지 않으면 UnsatisfiedDependencyException이 발생합니다.

AppContext 클래스에서 다음과 같이 MemberDao()를 주석 처리하고 애플리케이션을 실행합니다.

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

@Configuration
public class AppContext
{
//	@Bean
//	public MemberDao memberDao()
//	{
//		return new MemberDao();
//	}
}

에러 메시지는 Spring 버전에 따라서 차이가 있을 수 있습니다. Spring 5.3.2 버전에서는 다음 오류가 발생합니다.

org.springframework.context.support.AbstractApplicationContext refresh
경고: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberRegSvc': Unsatisfied dependency expressed through field 'memberDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'MemberDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'memberRegSvc': Unsatisfied dependency expressed through field 'memberDao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'MemberDao' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
더보기

@Autowired 어노테이션으로 자동 주입하려는 Bean 객체 타입이 설정 클래스에 여러 개 등록되어 있으면 어떤 일이 발생할까요?

설정 클래스 AppContext에서 memberPrinter를 두 개의 Bean으로 등록합니다.

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

@Configuration
public class AppContext
{
	@Bean
	public MemberPrinter memberPrinter()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberPrinter memberPrinterSeconds()
	{
		return new MemberPrinter();
	}
}

애플리케이션을 실행하면 expected single matching bean but found 2 오류가 발생합니다.

자동 주입하려는 Bean이 설정 클래스에 여러 개 등록되어 있어 한정 할 수 없다는 오류입니다.

 No qualifying bean of type 'MemberPrinter' available: expected single matching bean but found 2: memberPrinter,memberPrinterSeconds

자동 의존 주입 Bean을 선택하기 위한 @Qualifier

이전 테스트처럼 자동 주입이 가능한 Bean이 여러 개인 경우 Spring은 어떤 Bean을 사용할지 한정 할 수 없습니다.

Spring이 자동 주입에서 어떤 Bean을 선택할지를 알려주려면 @Qualifier을 사용합니다.

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppContext
{
	@Bean
	@Qualifier("printer")
	public MemberPrinter memberPrinter()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberPrinter memberPrinterSeconds()
	{
		return new MemberPrinter();
	}
}
코드 비고
Line 8:13 @Qualifier("printer") 설정 클래스에서 Bean을 등록하며 printer 한정자를 지정하고 있습니다.

@Qualifier로 지정한 Bean의 한정자는 자동 주입을 실행하는 위치에서 지정합니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.util.Collection;

public class MemberListPrinter
{
	@Autowired
	@Qualifier("printer")
	public void setMemberPrinter(MemberPrinter memberPrinter)
	{
		this.memberPrinter = memberPrinter;
	}
}
코드 비고
Line 8:13 @Qualifier("printer") 자동 주입을 위한 메소드에서 printer 한정자를 지정합니다.

이제 Spring이 자동 주입으로 어떤 Bean을 사용해야하는지 한정 할 수 있습니다. 애플리케이션이 오류 없이 정상 실행됩니다.

더보기

설정 클래스에서 Bean을 등록할 때 @Qualifier를 사용하지 않으면 디폴트로 Bean 이름을 한정자로 사용합니다.

예제의 AppContext에서 memberPrinterSecondsMemberPrinter에 대한 Bean 이름입니다.

자동 주입이 필요한 코드에서 Bean 이름을 한정자로 지정하면 애플리케이션이 정상 동작하는 것을 확인 할 수 있습니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

import java.util.Collection;

public class MemberListPrinter
{
	@Autowired
	@Qualifier("memberPrinterSeconds")
	public void setMemberPrinter(MemberPrinter memberPrinter)
	{
		this.memberPrinter = memberPrinter;
	}
}

상위-하위 타입 관계의 자동 주입

이전 예제에서 자동 주입이 가능한 Bean이 여러 개인 경우 Spring은 어떤 Bean을 사용할지 한정 할 수 없는 문제를 확인했습니다.

그렇다면 Bean으로 등록되는 클래스를 상속하는 클래스를 Bean으로 동시 등록했을 때는 어떤 결과가 나올까요?

새로운 클래스 MemberSummaryPrinter를 생성하고 MemberPrinter 클래스를 상속합니다.

public class MemberSummaryPrinter extends MemberPrinter
{
	@Override
	public void print(Member member)
	{
		System.out.printf("회원 정보: 이메일=%s, 이름=%s\n",
		                  member.getEmail(), member.getName());
	}
}

설정 클래스 AppContext에서 memberPrinterSeconds()의 리턴 타입을 수정합니다.

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppContext
{
	@Bean
	public MemberPrinter memberPrinter()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberSummaryPrinter memberPrinterSeconds()
	{
		return new MemberSummaryPrinter();
	}
}
코드 비고
Line 8:12 MemberPrinter MemberPrinter 클래스를 Bean에 등록합니다.
Line 14:18 MemberSummaryPrinter MemberSummaryPrinter 클래스를 Bean에 등록합니다. 이 클래스는 MemberPrinter를 상속합니다.

그리고 @Qualifier를 사용해 지정자를 사용 중인 코드 MemberInfoPrinter MemberListPrinter를 주석처리합니다.

애플리케이션을 실행하면 최초의 문제처럼 expected single matching bean but found 2 오류가 발생합니다.

더보기

Bean으로 등록된 클래스가 서로 다름에도 불구하고 상속 관계에 있을 경우 Spring은 자동 주입을 위해 어떤 Bean을 사용할 지 한정하지 못합니다.

상속 관계의 클래스 중 어떤 클래스를 자동 주입하기를 원하는지 Spring이 판단할 수 없기 때문입니다.

이 경우에도 @Qualifier 어노테이션을 등록하여 사용자가 자동 주입을 원하는 Bean을 한정해야 합니다.

선택적인(Optional) 자동 주입

어떤 비즈니스 로직에선느 자동 주입을 필요로 하는 객체가 선택적(Optional)일 수 있습니다.

테스트를 위해서 MemberPrinter 클래스를 다음과 같이 수정합니다.

import org.springframework.beans.factory.annotation.Autowired;

import java.time.format.DateTimeFormatter;

public class MemberPrinter
{
	private DateTimeFormatter dateTimeFormatter; 
	
	public void print(Member member)
	{
		if (null == dateTimeFormatter) 
		{
			System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
			                  member.getId(),
			                  member.getEmail(),
			                  member.getName(),
			                  member.getRegisterDateTime());
		}
		else
		{
			System.out.printf("회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
			                  member.getId(),
			                  member.getEmail(),
			                  member.getName(),
			                  dateTimeFormatter.format(member.getRegisterDateTime()));
		}
	}
	
	@Autowired
	public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter)
	{
		this.dateTimeFormatter = dateTimeFormatter;
	}
}
코드 비고
Line 9:27 print 비즈니스 로직에서 DateTimeFormatter는 Nullable합니다.
Line 29:33 @Autowired DateTimeFormatter 필드를 자동 주입합니다.

예시의 코드는 DateTimeFormatter는 Nullable합니다.

하지만 자동 주입으로 설정 되어 있기 때문에 Error creating bean with name 'memberPrinter' 오류가 발생합니다.

자동 주입 대상이 Nullable한 경우 requred = false 옵션을 사용합니다.

@Autowired(required = false)

Spring은 자동 주입 단계에서 설정 클래스에서 일치하는 Bean을 찾지 못하더도 오류를 발생시키지 않습니다.

더보기

Spring 5부터는 required = false와 동일한 기능을 자바 8java.util.Optional로 대체 할 수 있습니다.

import org.springframework.beans.factory.annotation.Autowired;

import java.time.format.DateTimeFormatter;
import java.util.Optional;

public class MemberPrinter
{
	private DateTimeFormatter dateTimeFormatter;
    
	@Autowired
	public void setDateTimeFormatter(Optional<DateTimeFormatter> dateTimeFormatter)
	{
		if (dateTimeFormatter.isPresent())
			this.dateTimeFormatter = dateTimeFormatter.get();
		else
			this.dateTimeFormatter = null;
	}
}

또 다른 방법은 Spring의 @Nullable을 사용하는 것입니다.

이 방법은 Optional과 동일한 결과를 갖으며 코드를 더 간결하게 만듭니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;

import java.time.format.DateTimeFormatter;

public class MemberPrinter
{
	private DateTimeFormatter dateTimeFormatter;
	
	@Autowired
	public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter)
	{
		this.dateTimeFormatter = dateTimeFormatter;
	}
}

이번에는 생성자에서 자동 의존 주입 대상을 초기화하고 requred = false 옵션을 활성화합니다.

import org.springframework.beans.factory.annotation.Autowired;

import java.time.format.DateTimeFormatter;

public class MemberPrinter
{
	private DateTimeFormatter dateTimeFormatter;
	
	public MemberPrinter()
	{
		dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
	}
	
	@Autowired(required = false)
	public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter)
	{
	    	System.out.println("@Autowired(required = false) invoked");
		this.dateTimeFormatter = dateTimeFormatter;
	}
}
코드 비고
Line 17 println 이 코드 라인을 실행되지 않습니다.

애플리케이션을 실행하면 생성자에서 할당된 DateTimeFormatter이 Setter로 인해 Null로 덮어씌우지 않습니다.

Spring에서 required = false 옵션을 지정하면 자동 할당에서 Bean을 찾지 못했을 때 Setter가 호출되지 않습니다.

이번에는 required = false 대신 @Nullable로 실행하고 이전과 비교합니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.lang.Nullable;

import java.time.format.DateTimeFormatter;

public class MemberPrinter
{
	private DateTimeFormatter dateTimeFormatter;
	
	public MemberPrinter()
	{
		dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
	}
	
	@Autowired
	public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter)
	{
		System.out.println("@Nullable invoked");
		this.dateTimeFormatter = dateTimeFormatter;
	}
}
코드 비고
Line 18 println 이 코드 라인은 실행되며 DateTimeFormatter는 Null로 할당됩니다.

생성자에서 할당된 DateTimeFormatter는 Setter가 실행됨에 따라서 Null로 덮어씌워집니다.

Spring은 @Nullable로 지정된 자동 할당에서 Bean을 찾지 못했을 때 Setter의 인자를 Null로 호출합니다.

자동 의존 주입과 명시적 의존 주입의 우선 순위

자동 의존 주입으로 지정되었으나 코드에서 DI를 명시하면 어떤 일이 발생할까요?

테스트를 위해서 설정 클래스 AppContext를 다음과 같이 수정합니다.

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

@Configuration
public class AppContext
{
	@Bean
	public MemberPrinter memberPrinter()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberSummaryPrinter memberPrinterSeconds()
	{
		return new MemberSummaryPrinter();
	}
	
	@Bean
	public MemberInfoPrinter infoPrinter()
	{
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		infoPrinter.setPrinter(memberPrinterSeconds());
		return infoPrinter;
	}
}
코드 비고
Line 7:11 memberPrinter() MemberPrinter를 Bean으로 등록합니다.
Line 13:17 memberPrinterSeconds() MemberSummaryPrinter를 Bean으로 등록합니다.
Line 23 setPrinter 의존 주입을 명시적으로 실행하며 MemberSummaryPrinter를 할당합니다.

MemberInfoPrinter 클래스는 다음과 같습니다.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class MemberInfoPrinter
{
	@Autowired
	@Qualifier("memberPrinter")
	public void setPrinter(MemberPrinter printer)
	{
		this.memberPrinter = printer;
	}
}
코드 비고
Line 6:11 setPrinter Setter는 DI에서 MemberPrinterMemberSummaryPrinter 모두를 사용 할 수 있습니다.
단, 자동 주입은 @Qualifier에 의해서 MemberPrinter Bean을 지정하고 있습니다.

애플리케이션을 실행하고 info 명령문을 실행합니다.

명령어를 입력하세요: 
$ info hello
회원 정보: 아이디=1, 이메일=hello, 이름=hello, 등록일=2023-11-27

명시적인 DI를 사용해 MemberSummaryPrinter를 할당하였으나 실제로는 MemberPrinter가 할당되었습니다.

Spring의 자동 주입이 더 높은 우선 순위로 처리되고 있습니다.

더보기

사실 DI와 자동 주입을 동시에 사용하는 것은 적절하지 않습니다. 어떤 Bean 객체가 실제로 할당 되었는지를 명확하게 알기 어려워 가독성이 떨어진다는 문제가 있습니다.

실제 작성되는 코드에서는 특정 Bean 객체 할당을 위해서 DI 또는 자동 주입을 일관성 있게 사용하는 것이 적합합니다.

정리 및 복습

  • 자동 의존 주입을 사용하면 DI를 위한 많은 코드를 생략 할 수 있습니다.
  • 자동 의존 주입을 위한 어노테이션은 @Autowired @Resource @Inject가 있습니다.
  • @Autowired로 자동 주입하는 객체가 설정 클래스에서 Bean으로 등록되어있지 않으면 Spring은 오류를 발생시킵니다.
  • @Autowired로 자동 주입하는 객체가 설정 클래스에서 여러 개의 Bean으로 등록되어 있으면 Spring은 오류를 발생시킵니다.
  • 여러 개의 Bean 중에서 자동 주입하려는 객체를 선택하려면 @Qualifier를 사용합니다.
  • @Qualifier는 자동 주입하려는 Bean 객체에 대한 지정자입니다.
  • 상속 관계에서도 Spring은 자동 주입으로 어떤 Bean을 사용해야하는지 판단하지 못합니다. 상속 관계 역시 자동 주입을 위해 @Qualifier로 지정해야 합니다.
  • 자동 주입이 필수적으로 실행되어야 하지 않으면 @Autowired(required=false)를 사용합니다. 또는 @Nullable과 자바 8의 Optional을 사용할 수 있습니다.
  • 설정 클래스에서 자동 주입하려는 Bean을 찾지 못했을 때 requred=false는 메소드가 실행되지 않습니다.
  • 반면 @Nullable로 지정된 메소드는 자동 주입하려는 Bean을 찾지 못했을 때 인자를 Null로 전달하며 실행됩니다.
  • 명시적인 DI와 자동 주입이 동시 실행되는 코드에서 자동 주입이 우선시됩니다.