Search

Chapter 4 의존 자동 주입

이전 까지 설정 클래스에서 주입할 의존 대상을 생성자나 메서드를 이용하여 직접 주입함.
자동 주입 : 스프링이 제공하는 기능으로 자동으로 의존하는 빈 객체를 주입해준다.

1. 예제 프로젝트 준비

이전 프로젝트 예제와 동일

2. @Autowired 애노테이션을 이용한 의존 자동 주입

@Autowired : 자동으로 이어준다 → 의존 객체를 명시하지 않아도 스프링이 필요한 의존 객체를 찾아서 주입.
필드와 메서드에 적용 가능
// 의존 객체 명시적으로 주입 @Bean public MemberDao memberDao() { return new MemberDao(); } @Bean public ChangePasswordService changePwdSvc() { ChangePasswordService pwdSvc = new ChangePasswordService(); pwdSvc.setMemberDao(memberDao()); // 의존 객체를 세터 메서드로 직접 주입함 return pwdSvc; }
Java
복사
// 의존 객체 자동 주입 @Bean public MemberDao memberDao() { return new MemberDao(); } @Bean public ChangePasswordService changePwdSvc() { ChangePasswordService pwdSvc = new ChangePasswordService(); return pwdSvc; // 자동 주입 기능을 사용하여 자동으로 주입함. }
Java
복사
필드
// 의존 객체 자동 주입을 위한 @Autowired 사용 public class ChangePasswordService { @Autowired // 의존을 주입할 대상 필드에 @Autowired 애노테이션을 붙인다. private MemberDao memberDao; // 스프링이 MemberDao 타입의 빈 객체를 찾아서 필드에 할당. ... }
Java
복사
메서드
public class ChangePasswordService { private MemberDao memberDao; @Autowired // 스프링이 메서드를 호출하고 파라미터의 타입에 해당하는 빈 객체를 찾아서 주입한다. public void setMemberDao(MemberDao memberDao){ this.memberDao = memberDao; } }
Java
복사

2.1 주입 대상이 없거나 여러개인 경우

주입 대상에 일치하는 빈이 없는 경우
NoSuchBeanDefinitionException 예외 발생
주입 대상에 일치하는 빈이 두 개 이상인 경우
NoUniqueBeanDefinitionException 예외 발생

3. @Qualifier 애노테이션을 이용한 의존 객체 선택

자동 주입 가능한 빈이 두 개 이상인 경우, 자동 주입할 빈을 지정하는 방법.
@Qualifier 애노테이션을 사용하여 자동 주입 대상 빈을 한정한다.
설정 클래스의 @Bean 애노테이션을 붙인 빈 설정 메서드에 사용
@Configuration public class AppCtx { // MemberPrinter 타입의 빈 메서드가 두 개 존재. @Bean @Qualifier("printer") public MemberPrinter memberPrinter1() { return new MemberPrinter(); } @Bean public MemberPrinter memberPrinter2() { return new MemberPrinter(); } }
Java
복사
@Autowired 애노테이션에서 자동 주입할 빈을 한정할 때 사용.
public class MemberListPrinter { private MemberPrinter printer; @Autowired @Qualifier("printer") // @Qualifier 애노테이션의 값이 printer인 memberPrinter1 빈을 찾아서 주입한다. public void setMemberPrinter(MemberPrinter printer) { this.printer = printer; } }
Java
복사

3.1 빈 이름과 기본 한정자

빈 설정에 @Qualifier 애노테이션이 없으면 빈 메서드의 이름을 한정자로 지정한다.
@Autowired 애노테이션도 @Qualifier 애노테이션이 없으면 필드나 파라미터 이름을 한정자로 사용한다.
@Configuration public class AppCtx { @Bean // @Qualifier 애노테이션이 없으므로 해당 빈의 한정자는 빈 메서드의 이름(printer) public MemberPrinter printer() { return new MemberPrinter(); } @Bean @Qualifier("mprinter") public MemberPrinter printer2() { return new MemberPrinter(); } }
Java
복사
public class MemberInforPrinter2 { @Autowired // @Qualifier 애노테이션이 없으므로 필드의 이름을 한정자로 사용 (printer) private MemberPrinter printer; }
Java
복사

4. 상위/하위 타입 관계와 자동 주입

MemberPrinter 클래스를 상속한 MemberSummaryPrinter 클래스를 정의하고 설정 클래스에 MemberPrinter와 MemberSummaryPrinter 빈을 설정한다.
public class MemberSummaryPrinter extends MemberPrinter { ... }
Java
복사
@Configuration public class AppCtx { @Bean public MemberPrinter memberPrinter1() { return new MemberPrinter(); } @Bean public MemberSummaryPrinter memberPrinter2() { return new MemberPrinter(); } }
Java
복사
MemberPrinter 빈을 주입하는 세터 메서드에 @Autowired 애노테이션만 작성하고 실행한다.
public class MemberInfoPrinter { private MemberPrinter printer; @Autowired public void setMemberPrinter(MemberPrinter printer) { this.printer = printer; } }
Java
복사
동일한 빈이 두 개 이상일 때 발생하는 NoUniqueBeanDefinitionException 예외가 발생한다.
하위 클래스는 상위 클래스 타입에도 할당할 수 있으므로 스프링은 어떤 빈을 자동 주입해야하는지 알 수 없다.
@Qualifier 애노테이션을 사용하여 주입할 빈을 한정한다.
public class AppCtx { @Bean @Qualifier("printer") public MemberPrinter memberPrinter1() { return new MemberPrinter(); } @Bean @Qualifier("summaryPrinter") public MemberSummaryPrinter memberPrinter2() { return new MemberSummaryPrinter(); } } //////////////////////////////////////////// public class MemberInfoPrinter { private MemberPrinter printer; @Autowired @Qualifier("printer") public void setMemberPrinter(MemberPrinter printer) { this.printer = printer; } } //////////////////////////////////////////// public class MemberListPrinter { private MemberPrinter printer; @Autowired @Qualifier("summaryPrinter") public void setMemberPrinter(MemberPrinter printer) { this.printer = printer; } // OR @Autowired // 파라미터 타입을 MemberSummaryPrinter로 지정한다. public void setMemberPrinter(MemberSummaryPrinter printer) { this.printer = printer; } }
Java
복사

5. @Autowired 애노테이션의 필수 여부

@Autowired 애노테이션 사용 시 주입 대상 빈 이 존재하지 않는 경우 예외 발생
@Autowired(required = false) 를 사용하여 주입 대상 빈이 존재하지 않는 경우 자동 주입하지 않는다.
public class MemberPrinter { private DateTimeFormatter dateTimeFormatter; @Autowired(required = false) // DateTimeFormatter 타입의 빈이 없으면 세터 메서드를 호출하지 않으므로 예외가 발생하지 않는다. // dateTimeFormatter 필드의 값은 아무것도 할당되지 않음. ( null 이 아님!! ) public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter) { this.dateTimeFormatter = dateTimeFormatter; }
Java
복사
@Autowired 애노테이션의 required 속성을 false로 지정하면 매칭되는 빈이 없어도 예외가 발생하지 않으며 자동 주입을 실행하지 않는다.
의존 주입 대상에 Optiona을 사용하면 매칭되는 빈이 없어도 예외가 발생하지 않는다.
public class MemberPrinter { private DateTimeFormatter dateTimeFormatter; @Autowired // 매칭되는 빈이 없어도 예외가 발생하지 않는다. public void setDateTimeFormatter(Optional<DateTimeFormatter> formatterOpt) { if(formatterOpt.isPresent()) { this.dateTimeFormatter = formatterOpt.get(); } else { // 일치하는 빈이 없는 경우 값이 없는 Optional을 전달. this.dateTimeFormatter = null; // 명시적으로 필드에 null 할당. } }
Java
복사
@Nullable 애노테이션을 사용하여 매칭되는 빈이 없는경우 null을 인자로 전달한다.
public class MemberPrinter { private DateTimeFormatter dateTimeFormatter; @Autowired // 매칭되는 빈이 없어도 예외가 발생하지 않으며 null 값을 전달한다. public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter) { this.dateTimeFormatter = dateTimeFormatter; }
Java
복사
정리
자동 주입 필수 여부를 정하는 방법
1.
@Autowired(required = false)
매칭되는 빈이 없는 경우 필드에 값을 할당하지 않고 메서드는 실행하지 않는다.
2.
Optional<T> 사용
매칭되는 빈이 없는 경우 값이 없는 Optional 을 할당한다.
3.
@Nullable 사용
매칭되는 빈이 없는 경우 null 을 할당한다.

6. 자동 주입과 명시적 의존 주입 간의 관계

설정 클래스에서 명시적으로 주입한 후 자동 주입이 되면 어떻게 될까?
결론 : 자동 주입 >> 명시적 주입
public class AppCtx { @Bean @Qualifier("printer") public MemberPrinter memberPrinter1() { return new MemberPrinter(); } @Bean @Qualifier("summaryPrinter") public MemberSummaryPrinter memberPrinter2() { return new MemberSummaryPrinter(); } @Bean public MemberInfoPrinter infoPrinter() { MemberInfoPrinter infoPrinter = new MemberInfoPrinter(); infoPrinter.setPrinter(memberPrinter2()); // 명시적 의존 주입 return infoPrinter; }
Java
복사
public class MemberInfoPrinter { @Autowired @Qualifier("printer") public void setPrinter(MemberPrinter printer) { this.printer = printer; } }
Java
복사
@Autowired 애노테이션을 사용한 경우, 설정 클래스에서 주입하기 보다는 자동 주입을 사용하는 편이 낫다.
자동 주입과 명시적 주입이 섞여있는 경우 NullPointerException 예외 처리에 어려움이 생긴다.
의존 자동 주입을 사용한다면 일관적으로 자동 주입을 사용하도록 하자.