@Bean
public MemberDao memberDao() {//생성자 주입 방법
return new MemberDao();
}
@Bean
public ChangePasswordService changePwdSvc() {//setter 메소드 주입 방법
ChangePasswordSerivce pwdSvc = new ChangePasswordSerivce();
pwdSvc.setMemberDao(memberDao());
return pwdSvc;
}
@Bean
public MemberDao memberDao() {//생성자 주입 방법
return new MemberDao();
}
@Bean
public ChangePasswordService changePwdSvc() {//setter 메소드 주입 방법
ChangePasswordSerivce pwdSvc = new ChangePasswordSerivce();
return pwdSvc;
}
package spring;
import org.springframework.beans.factory.annotation.Autowired;
public class ChangePasswordService {
@Autowired
private MemberDao memberDao;
public void changePassword(String email, String oldPwd, String newPwd) {
Member member = memberDao.selectByEmail(email);
if (member == null)
throw new MemberNotFoundException();
member.changePassword(oldPwd, newPwd);
memberDao.update(member);
}
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
}
스프링 컨테이너가 자동 주입을 하려면 해당 타입을 가진 빈이 어떤 빈인지 정확하게 한정할 수 있어야한다.
주입할 타입의 빈이 2개이거나 없는 경우 스프링은 자동 주입에 실패하고 익셉션을 발생 시킨다.
자동 주입 가능한 빈이 2개 이상이면 @Qualifier 애노테이션을 사용해서 자동 주입 대상 빈을 한정한다.
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
@Autowired
@Qualifier("printer")
public void setMemberPrinter(MemberPrinter printer) {
this.printer = printer;
}
@Bean
public MemberPrinter printer1() {
return new MemberPrinter();
}
@Bean
@Qualifier("mprinter")
public MemberPrinter printer2() {
return new MemberPrinter();
}
@Bean
public MemberInfoPrinter2 infoPrinter() {
MemberInfoPrinter2 infoPrinter = new MemberInfoPrinter2();
return infoPrinter;
}
package spring;
public class MemberSummaryPrinter extends MemberPrinter {
@Override
public void print(Member member) {
System.out.printf(
"회원 정보: 이메일=%s, 이름=%s\n",
member.getEmail(), member.getName());
}
}
위 클래스는 MemberPrinter 클래스를 상속한 MemberSummaryPrinter클래스이다.
AppCtx클래스 설정에서 memberPrinter2() 메서드가 MemberSummaryPrinter 타입의 빈 객체를 설정하도록 변경하자.
@Bean
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
우선 AppCtx 설정 파일에 @Qualifier 애노테이션을 이용해서 주입할 빈을 한정한다.
package config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import spring.MemberPrinter;
@Configuration
public class AppCtx {
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
}
package spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public class MemberInfoPrinter {
private MemberDao memDao;
private MemberPrinter printer;
public void printMemberInfo(String email) {
Member member = memDao.selectByEmail(email);
if (member == null) {
System.out.println("데이터 없음\n");
return;
}
printer.print(member);
System.out.println();
}
@Autowired
public void setMemberDao(MemberDao memberDao) {
this.memDao = memberDao;
}
@Autowired
@Qualifier("printer")
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
- MemberInfoPrinter 클래스에서 사용되는 MemberPrinter 빈 객체를 한정해주고, 한정자를 이용해서 자동 주입해준다.
> MemberListPrinter 클래스에 자동 주입할 MemberPrinter 타입 빈 주입하는 방법 1
```java
@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
public MemberListPrinter() {
}
...
@Autowired
@Qulifier("summaryPrinter")
public void setMemberPrinter(MemberPrinter printer) {//이부분이 다르다.
this.printer = printer;
}
}
MemberListPrinter 클래스에 자동 주입할 MemberPrinter 타입 빈 주입하는 방법 2
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
public MemberListPrinter() {
}
...
@Autowired
public void setMemberPrinter(MemberSummaryPrinter printer) {//이 부분에 상속 받는 MemberSummaryPrinter 클래스 사용
this.printer = printer;
}
}
- MemberSummaryPrinter 타입 빈은 한 개만 존재하므로 MemberSummaryPrinter 빈을 자동 주입 받도록 수정한다.
- 자동 주입 대상이 두개 이상이어서 발생하는 문제 피할 수 있다.
- MemberSummaryPrinter 클래스는 MemberPrinter 클래스를 상속 받기 때문에 사용할 수 있다.
### @Autowired 애노테이션의 필수 여부
```java
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public MemberPrinter() {
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
}
public void print(Member member) {
if (dateTimeFormatter == null) {
System.out.printf(
"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
member.getId(), member.getEmail(),
member.getName(), member.getRegisterDateTime());
} else {
System.out.printf(
"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n",
member.getId(), member.getEmail(),
member.getName(),
dateTimeFormatter.format(member.getRegisterDateTime()));
}
}
// 빈객체가 없는 경우 에러를 발생시킬 수 있다.
@Autowired
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
//첫번째 방법
public class MemberPrinter {
...//필드
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
//첫번째 방법 필드에서
public class MemberPrinter {
@Autowired(required=false)
private DateTimeformatter dateTimeFormatter;
public void print(Member member) {
...
}
}
//두번째 방법
public class MemberPrinter {
...//필드
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
if (formatterOpt.isPresent()) {
this.dateTimeFormatter = formatterOpt.get();
} else {
this.dateTimeFormatter = null;
}
}
}
//두번째 방법 필드에서
public class MemberPrinter {
@Autowired
private Optional<DateTimeformatter> formatterOpt;
public void print(Member member) {
DateTimeFormatter dateTimeFormatter = formatterOpt.orElse(null);
if(dateTimeFormatter == null) {
...
} else {
...//여기에서 사용하는것이 정상적일 것
}
}
}
//세번째 방법
public class MemberPrinter {
...//필드
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
//세번째 방법 필드에서
public class MemberPrinter {
@Autowired
@Nullable
private DateTimeformatter dateTimeFormatter;
public void print(Member member) {
...
}
}
@Nullable 과 @Autowired(required = false) 의 차이점
- 일치하는 빈이 없으면 값 할당 자체를 하지않는 @Autowired(required=false)
- @Nullable 애노테이션을 사용하면 일치하는 빈이 없을 때 null값을 할당한다.
- Optional 타입은 매칭 되는 빈이 없으면 값이 없는 Optional을 할당한다.
- ✅ 기본 생성자에서 자동 주입 대상이 외는 필드를 초기화할 때는 이 점에 유의해야한다.
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public MemberPrinter() {//생성자에서 필드값을 초기화 해준다.
dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
}
public void print(Member member) {
if (dateTimeFormatter == null) {
System.out.printf(
"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
member.getId(), member.getEmail(),
member.getName(), member.getRegisterDateTime());
} else {
System.out.printf(
"회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n",
member.getId(), member.getEmail(),
member.getName(),
dateTimeFormatter.format(member.getRegisterDateTime()));
}
}
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
// 출력 : 2018년 01월 02일 null값 전달 X
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
if (formatterOpt.isPresent()) {
this.dateTimeFormatter = formatterOpt.get();
} else {
this.dateTimeFormatter = null;
}
}
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
// 출력 : 2018-01-02 null값 전달 O
}
설정 클래스에서 의존을 주입했는데 자동 주입이 대상이라면?
@Configuration
public class AppCtx {
...
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
@Bean
public MemberListPrinter listPrinter() {
return new MemberListPrinter();
}
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
//memberPrinter2 빈을 주입
infoPrinter.setPrinter(memberPrinter2());
return infoPrinter;
}
...
}
public class MemberInfoPrinter {
...
@Autowired
@Qualifier("printer")
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
의존 자동주입과 수동주입을 함께 사용하는 것도 바람직하지 않다.
자동 주입을 사용한다면 일관성있게 지속적으로 @Autowired를 이용한 자동 주입을 사용하는 것을 권장한다.