@Autowired Annotation란?
스프링에서 지원하는 자동 주입 기능이다.
스프링이 알아서 의존 객체를 찾아서 주입한다.
즉, 스프링이 필요한 의존 빈 객체를 찾아서 주입해준다.
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public ChangePasswordService changePwdSvc(){
ChangePasswordService pwdSvc = new ChangePasswordService();
// 의존 객체 주입
pwdSvc.setMemberDao(memberDao());
return pwdSvc;
}
위 코드에서 스프링의 자동 주입 기능을 사용하지 않는다면 pwdSvc.setMemberDao(memberDao()); 코드처럼 직접 의존 객체를 명시해야 한다.
하지만, 의존을 주입할 대상에 @Autowired 어노테이션을 붙여준다면 pwdSvc.setMemberDao(memberDao()); 코드를 명시할 필요가 없어진다.
@Autowired 를 붙여주기 위해 ChangePasswordService 클래스로 가보자.
import....anootation.Autowired;
public class ChangePasswordService{
@Autowired
private MemberDao memberDao;
public void changePassword(String email, Strin oldPwd, String newPwd) {
...
}
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
}
ChangePasswordService 클래스에서 MemberDao 필드에 @Autowired 를 붙여주었다.
이렇게 어노테이션을 붙여주면 설정 클래스에서 의존을 주입하지 않아도 된다.
@Autowired를 붙여준 이상 스프링이 알아서 해당 타입의 빈 객체를 찾아서 필드에 할당하기 때문이다.
그렇다면 AppCtx 클래스의 @Bean 설정 메서드에서 의존을 주입하는 코드를 삭제하자.
@Configuration
public class AppCtx {
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
...
@Bean
public ChangePasswordService changePwdSvc(){
ChangePasswordService pwdSvc = new ChangePasswordService();
return pwdSvc;
}
}
@Autowired 어노테이션은 메서드에도 붙일 수 있다.
import ... .annotation.Autowired;
public class MemberInfoPrinter {
private MemberDao memrDao;
private MemberPrinter printer;
public void printMemberInfo(String email) {
...
}
@Autowired
public void setMemberDao(MemberDao memberDao) {
this.memrDao = memberDao; // 필드에 할당
}
@Autowired
public void setPrinter(MemberPrinter printer) {
this.printer = printer; // 필드에 할당
}
}
@Autowired를 통해 setMemberDao / setPrinter 메서드에 일치하는 타입인 MemberDao 빈을 스프링 컨테이너에서 찾고 주입해주는 것이다.
setMemberDao / setPrinter 메서드에 주입하는 것이므로 세터 주입 방법을 사용하고 있는 것이다.
각각 @Autowired를 통해 타입이 일치하는 빈 객체를 찾아서 주입하고 있다.
일치하는 Bean이 없는 경우
위 코드에서 주로 MemberDao의 빈을 찾아서 의존을 주입하고 있다.
만약 MemberDao가 없는 경우 'No qulifying bean of type 'spring.MemberDao' available...' 에러가 발생한다.
적용할 수 았는 MemberDao타입의 빈이 없다는 에러 문구를 띄운 것이다.
만약 일치하는 빈이 두 개 이상이면 'No qulifying bean of type 'spring.MemberPrinter' available: expected single matching bean but found 2...' 라는 에러가 발상한다.
이는 어떤 빈인지 정확하게 한정할 수 있어야 하지만 해당 타입의 빈이 두개이기 때문에 어떤 빈을 자동 주입 대상으로 선택해야 할지 한정할 수 없기 때문이다.
이를 해결하기 위해서 @Qualifier를 사용한다.
@Qualifier를 이용한 의존 객체 선택
자동 주입이 가능한 빈이 2개 이상일 때 사용한다.
@Configuration
public class AppCtx {
...
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1(){
return new MemberPrinter();
}
@Bean
public MemberPrinter memberPrinter2(){
return new MemberPrinter();
}
}
memberPrinter1() 메서드에 @Qualifier를 사용하여 "printer" 값을 갖도록 한다.
이렇게 지정한 한정값은 @Autowired 에서 자동 주입할 빈을 한정할 때 사용한다.
public class MemberListPrinter {
private MemberDao memrDao;
private MemberPrinter printer;
@Autowired
@Qualifier("printer")
public void setMemberPrinter(MemberPrinter printer) {
this.printer = printer;
}
}
setMemberPrinter() 메서드는 @Autowired로 인해 MemberPrinter 타입의 빈을 자동 주입하게 된다.
이때 @Qualifier 값이 printer이므로 한정 값이 printer인 빈(memberPrinter1)을 사용하게 된다.
만약 @Qualifier가 없다면 빈 이름을 한정자로 지정한다.
@Configuration
public class AppCtx {
...
@Bean
public MemberPrinter printer(){
return new MemberPrinter();
}
...
}
그러므로, printer() 메서드의 한정자 이름은 "printer"가 되는 것이다.
상위/하위 타입 관계와 자동 주입
public class MemberSummaryPrinter extends MemberPrinter {
@Overried
public void print(Member member) {
...
}
}
위 코드에서 MemberSummaryPrinter는 MemberPrinter를 상속하고 있다.
@Configuration
public class AppCtx {
...
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1(){
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2(){
return new MemberSummaryPrinter();
}
}
Application Context 클래스에서 memberPrinter2 빈을 MemberSummaryPrinter 타입으로 변경했다.
하지만, MemberPrinter 타입이 2개이며 @Qualifier를 설정하지 않았을 때의 오류가 발생한다.
왜냐하면 MemberSummaryPrinter 클래스가 MemberPrinter를 상속받기 때문이다.
MemberSummaryPrinter 클래스는 MemberPrinter 타입에도 할당할 수 있으므로 memberPrinter1 과 memberPrinter2 중 어떤 빈을 주입해야 하는지 구별할 수 없는 것이다.
그러므로 MemberSummaryPrinter에도 @Qualifier를 설정하여 구별할 수 있도록 한다.
@Autowired 필수 여부
자동 주입할 대상이 필수가 아닌 경우 3가지의 처리 방법이 있다.
1. required 속성을 flase로 지정한다.
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
빈이 없어도 exception을 발생하지 않고 자동 주입을 수행하지 않는다.
2. Optional
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public void print(Member member) {
...
}
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
if(formatterOpt.isPresent()) { // 값이 존재 한다면
this.dateTimeFormatter = formatterOpt.get(); // 할당
} else {
this.dateTimeFormatter = null; // null 할당
}
}
}
자바 8의 Optional을 사용하여 빈이 존재하지 않는다면 Optional을 인자로 전달한다.
전달 시 exception이 발생하지 않는다.
3. @Nullable
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
public void print(Member member) {
...
}
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
}
@Nullable은 빈이 존재하지 않아도 메서드가 호출된다.
@Autowired(required = false)는 빈이 존재하지 않으면 메서드가 호출되지 않는다.
'Spring' 카테고리의 다른 글
스프링 DI (0) | 2023.06.20 |
---|---|
싱글톤 Singleton (0) | 2023.06.19 |
[Spring] DI, IoC (0) | 2023.05.09 |