의존 대상을 설정 코드에서 직접 주입하지 않고 스프링이 자동으로 의존하는 빈 객체를 주입해주는 기능도 있다. 이를 자동 주입이라고 한다.
ChangePasswordService
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;
}
}
@Autowired 애노테이션을 붙이면 설정 클래스에서 의존을 주입하지 않아도 된다.
스프링이 해당 타입의 빈 객체를 찾아서 필드에 할당한다.
@Autowired 애노테이션을 memberDao 필드에 붙였으므로 AppCtx 클래스의 @Bean 설정 메서드에서 의존을 주입하는 코드를 삭제하면 된다.
@Autowired 애노테이션은 메서드에도 붙일 수 있다.
MemberInfoPrinter
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
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
AppCtx
@Configuration
public class AppCtx {
...
@Bean
public MemberInfoPrinter infoPrinter() {
MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
return infoPrinter;
}
}
빈 객체의 메서드에 @Autowired 애노테이션을 붙이면 스프링은 해당 메서드를 호출한다. 이때 메서드 파라미터 타입에 해당하는 빈 객체를 찾아 인자로 주입한다.
MemberRegisterService
public class MemberRegisterService {
@Autowired
private MemberDao memberDao;
public MemberRegisterService() {
}
public MemberRegisterService(MemberDao memberDao) {
this.memberDao = memberDao;
}
...
}
MemberListPrinter
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
public MemberListPrinter() {
}
...
@Autowired
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@Autowired
public void setMemberPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
AppCtx
@Configuration
public class AppCtx {
@Bean
public MemberDao memberDao() {
return new MemberDao();
}
@Bean
public MemberRegisterService memberRegSvc() {
return new MemberRegisterService(memberDao());
}
@Bean
public ChangePasswordService changePwdSvc() {
return new ChangePasswordService();
}
@Bean
public MemberPrinter memberPrinter() {
return new MemberPrinter();
}
@Bean
public MemberListPrinter listPrinter() {
return new MemberListPrinter();
}
@Bean
public MemberInfoPrinter infoPrinter() {
return new MemberInfoPrinter();
}
}
일치하는 빈이 없는 경우
@Autowired 애노테이션을 적용한 대상에 일치하는 빈이 없으면 에러가 난다.@Autowired 애노테이션을 붙인 주입 대상에 일치하는 빈이 두 개 이상이어도 에러가 난다.
자동 주입이 가능한 빈이 두 개 이상이면 자동 주입할 빈을 지정할 수 있는 방법이 필요하다. @Qualifier 애노테이션을 사용하면 자동 주입 대상 빈을 한정할 수 있다.
@Configuration
public class AppCtx {
@Bean
@Qualifier("printer")
public MemberDao memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("printer")
public MemberDao memberPrinter2() {
return new MemberPrinter();
}
}
MemberListPrinter
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
...
@Autowired
@Qualifier("printer")
public void setMemberPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
setMemberPrinter() 메서드에 @Autowired 애노테이션을 붙였으므로 MemberPrinter탕비의 빈을 자동 주입한다. 이때 @Qualifier 값이 "printer"이므로 한정 값이 "printer"인 빈을 의존 주입 후보로 사용한다.
빈 설정에 @Qualifier 애노테이션이 없으면 빈의 이름을 한정자로 지정한다.
``` java
@Configuration
public class AppCtx {
@Bean
public MemberPrinter printer() {
return new MemberPrinter();
}
@Bean
@Qualifier("mprinter")
public MemberPrinter printer12() {
return new MemberPrinter();
}
@Bean
public MemberInfoPrinter2 infoPrinter() {
MemberInfoPrinter2 infoPrinter = new MemberInfoPrinter2();
return infoPrinter;
}
}
printer()메서드로 정의한 빈의 한정자는 빈 이름인 "printer"가 된다. printer2 빈은 @Qualifier 애노테이션 값인 "mprinter"가 한정자가 된다.
public class MemberInfoPrinter2 {
@Autowired
private MemberPrinter printer;
@Autowired 애노테이션도 @Qualifier 애노테이션이 없으면 필드나 파라미터 이름을 한정자로 사용한다. 예를 들어 위의 코드는 pinter 필드에 일치하는 빈이 두 개 이상 존재하면 한정자로 필드 이름인 "printer"를 사용한다.
자동 주입할 대상이 필수가 아닌 경우네는 @Autowired 애노테이션의 required 속성을 다음과 같이 false로 지정하면 된다. @Autowired 애노테이션의 required 속성을 false로 지정하면 매칭되는 빈이 없어도 익셉션이 발생하지 않으며 자동 주입을 수행하지 않는다. 스프링 5 버전부터는 @Autowired 애노테이션의 required 속성을 false로 하는 대신 의존 주입 대상에 자바 8의 Optional을 사용해도 된다.
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
...
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
Optional 이용하기
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
...
public void setDateFormatter(Optional<DateTimeFormatter> dateTimeFormatter) {
if (dateTimeFormatter.isPresent()) {
this.dateTimeFormatter = dateTimeFormatter.get();
} else{
this.dateTimeFormatter = null;
}
}
}
자동 주입 대상 타입이 Optional인 경우, 일치하는 빈이 존재하지 않으면 값이 없는 Optional을 인자로 전달하고, 일치하는 빈이 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달한다.
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
...
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
@Autowired 애노테이션을 붙인 세터 메서드에서 @Nullable 애노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동 중비할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 인자로 null을 전달한다.
설정 클래스에서 세터 메서드를 통해 의존을 중비해도 해당 세터 메서드에 @Autowired 애노테이션이 붙어 있으면 자동 주입을 통해 일치하는 빈을 주입한다. 따라서 @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 주입하기보다는 스프링이 제공하는 자동 주입 기능을 사용하는 편이 낫다.