Chap 4. 의존 자동 주입
@Autowire 애노테이션을 이용한 의존 자동 주입
자동 주입 기능을 사용하면 스프링에서 데이터 타입에 맞춰 의존객체(빈(Bean) 객체)를 찾아 주입한다.
@Autowired 애노테이션은 메서드에도 붙일 수 있다.
@Qualifier 애노테이션을 이용한 의존 객체 선택
자동 주입 가능한 빈이 두 개 이상이면 자동 주입할 빈을 지정할 수 있는 방법이 필요하다. 이때 @Qualifier를 사용한다. 이것은 주입 대상 빈을 한정하기 위해 사용한다.
@Configuration
public class AppCtx {
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberPrinter memberPrinter2() {
return new MemberPrinter();
}
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
@Autowired
public void setMemberDao(MemberDao memberDao) {
this.memberDao = memberDao;
}
@Autowired
@Qualifier
public void setPrinter(MemberPrinter printer) {
this.printer = printer;
}
MemberPrinter가 2개이므로 setPrinter에서 어떤 프린터를 써야될지 자동으로 판단할 수가 없다. 하지만 @Qualifier를 사용하면 memberPrinter1과 setPrinter가 이어지도록 설정 할 수 있다.
@Autowired 애노테이션을 필드와 메서드에 모두 적용 할 수 있으므로 @Qualifier 애노테이션도 필드와 메서드에 적용가능하다.
빈 설정에 @Qualifier가 없으면 빈 이름을 한정자로 지정한다.
@Configuration
public class AppCtx {
@Bean
public MemberPrinter printer1() {
return new MemberPrinter();
}
@Bean
@Qualifier("mprinter")
public MemberPrinter printer2() {
return new MemberPrinter();
}
printer1() 메서드의 빈 한정자는 빈 이름인 " printer1"가 되고, printer2() 빈은 @Qualifier 애노테이션 값은 "mprinter"가 한정자가 된다.
상위 / 하위 타입관계와 자동 주입
상속 시 자식 클래스도 부모 클래스 타입에 포함된다. 즉, 부모의 클래스 타입이 자식의 클래스 타입이 되어 @Qualifier와 같은 한정자가 없을 때, @Autowired를 할 때 데이터 타입이 같은 객체가 2개로 인식되어 오류가 발생한다. 따라서 부모 자식간의 상속이 발생한 경우 한정자를 필수적으로 사용해야된다.
@Autowired 애노테이션 필수 여부
다음은 dateTimeFormatter 필드의 null 여부에 따라 날짜형식이 다르게 출력되는 코드이다.
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)//함수를 실행하지 않아 기본생성자 초기화 값을 null로 생성하지 않는다.
public void setDateFormatter(DateTimeFormatter dateTimeFormatter)
{
this.dateTimeFormatter = dateTimeFormatter;
}
// @Autowired
// public void setDateFormaatter(@Nullable DateTimeFormatter dateTimeFormatter)//함수를 실행하고 확인하여 bean이 없으면 기본생성자 초기화 값이 null이 된다.
// {
// this.dateTimeFormatter = dateTimeFormatter;
// }
}
@Autowired 애노테이션은 기본적으로 @Autowired 애노테이션을 붙인 타입에 해당하는 빈이 존재하지 않으면 오류가 발생한다.
필수여부 지정에는 크게 2가지가 있다. (Optional까지 포함하면 3가지지만, 다루지 않겠다.)
첫번째는 @Autowired 애노테이션의 required 속성을 false로 지정하는 것이다. 다음과 같이 설정을 하면 함수 자체를 실행 하지 않아서 위 예제의 this.dateTimeFormatter에 값이 들어가지 않아 yyyy년 MM월 dd일의 형식으로 출력된다.
두번째는 @Nullable 애노테이션을 사용하는 것이다. 다음과 같이 설정하면 자동 주입할 빈이 존재하지 않아도 함수를 실행하여 위 예제의 this.dateTimeFormatter에 null값이 들어가서 yyyy-MM-dd의 형식으로 출력된다.
자동 주입과 명시적 의존 주입 간의 관계
설정 클래스에서 의존 주입(setter)을 하고, 자동 주입 대상(@Autowired)이 되면 주입이 2번 발생한 것으로 간주한다. 하지만 @Autowired 애노테이션이 우선권을 가진다.
의존주입과 자동 주입을 섞어서 사용하면 휴먼에러가 발생할 가능성이 매우 높으므로 스프링에서 제공하는 자동 주입 기능(@Autowired)으로 통일하는것을 추천한다.