이전 까지 설정 클래스에서 주입할 의존 대상을 생성자나 메서드를 이용하여 직접 주입함.
자동 주입 : 스프링이 제공하는 기능으로 자동으로 의존하는 빈 객체를 주입해준다.
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 예외 처리에 어려움이 생긴다.
•
의존 자동 주입을 사용한다면 일관적으로 자동 주입을 사용하도록 하자.