2021-05-29글
@Autowired 어노테이션을 통한 의존 자동 주입
- 자동 주입 기능을 사용하면 스프링이 알아서 의존 객체를 찾아 주입한다.
- 사용 방법은 의존을 주입할 대상에
@Autowiwred
어노테이션을 붙이면 된다. - 해당 어노테이션이 붙어있으면 스프링이 이를 찾아 필드에 할당한다.
*@Autowired를 적용한 대상에 일치하는 빈이 없으면? *
해당 필드에 대한 의존을 충족하지 않는다는 내용과 함께 빈이 존재하지 않는다는 에러 메시지가 출력된다.
*만약 두개 이상이면? *
해당 타입의 빈이 한개가 아닌 여러개를 발견했다는 예외 메시지가 출력된다.
@Qualifier를 이용한 의존 객체 선택
- 자동 주입 가능한 빈이 두 개 이상인 경우 자동 주입을 할 빈을 지정하는 방법
사용 위치
@Bean 을 붙인 빈 설정 메서드
@Configuration
public class ApplicationContextTestResourceQualifier {
@Bean
@Qualifier("defaultFile")
public File defaultFile() {
File defaultFile = new File("defaultFile.txt");
return defaultFile;
}
}
@Autowired에서 자동 주입할 빈을 한정할 때 사용
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
public MemberListPrinter(MemberDao memberDao, MemberPrinter printer) {
this.memberDao = memberDao;
this.printer = printer;
}
@Autowired
@Qualifier("printer")
public void setMemberPrint(MemberPrinter printer) {
this.printer = printer;
}
}
- @Qualifier의 속성으로 주입할 빈의 후보를 한정한다.
- 빈 설정에 해당 어노테이션이 없으면 빈의 이름을 한정자로 지정한다.
상위/하위 타입 관계와 자동 주입
MemberPrinter 클래스를 상속한 MeberSummaryPrinter 클래스가 있다고 하자.
public class MemberSummaryPrinter extends MemberPrinter {
@Override
public void print(Member member) {
System.out.printf(
"회원 정보: 이메일=%s, 이름=%s\n",
member.getEmail(), member.getName());
}
}
그리고 AppCtx 설정에서 memberPrinter2()
가 의 빈 객체를 설정하도록 변경한다.
@Configuration
public class AppCtx {
// ...
@Bean
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
}
memberPrinter2()
의 빈을 MemberSummaryPrinter으로 변경해도 빈 중복 에러가 발생한다.- MemberSummaryPrinter는 MemberPrinter 타입에도 할당할 수 있기 때문이다.
- 스프링 컨테이너는 MemberPrinter 타입 빈을 자동 주입해야하는 @Autowired를 만나면 `memberPrinter1(), memberPrinter2() 중 어떤 빈을 주입해야하는 지 알 수 없다.
이 문제는 두가지 방법으로 처리할 수 있다.
@Qualifier
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
MemberListPrinter가 MemberSummaryPrinter를 사용하도록 (Composition)
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
// ...
@Autowired
public void setMemberPrinter(MemberSummaryPrinter printer) {
this.printer = printer;
}
}
@Autowired의 필수 여부 지정 방법
public class MemberPrinter {
private DateTimeFormatter dateTimeFormatter;
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;
}
// ...
dateTimeFormatter가 null인지 여부에 따라 날짜 형식을 바꿔 출력한다.
즉 반드시 setDateFormatter()
를 통해 의존 객체를 주입할 필요가 없다.
하지만 @Autowired
는 해당하는 빈이 존재하지 않으면 예외를 발생시킨다.
@Autowired(required = false)
이 경우 @Autowired(required = false)
설정으로 자동 주입 대상이 필수가 아님을 명시한다.
이러면 매칭되는 빈이 없어도 예외가 발생하지 않고 자동 주입을 수행하지 않는다.
@Autowired(required = false)
public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
Optional
스프링 5부터는 required 속성 말고, Optional을 사용할 수도 있다.
@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
if (formatterOpt.isPresent()) {
this.dateTimeFormatter = formatterOpt.get();
} else {
this.dateTimeFormatter = null;
}
}
@Nullable
해당 어노테이션을 의존 주입 대상 파라미터에 붙이면 세터 메서드를 호출할 때 자동 주입할 빈이 존재할 경우 인자로 받고, 존재하지 않으면 null을 전달한다.
이 어노테이션은 스프링이 제공하는 어노테이션이다.
@Autowired
public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
required 속성을 false로 할 때와 차이점?
@Nullable 어노테이션을 사용하면 자동 주입할 빈이 존재하지 않아도 세터 메서드가 호출된다.
@Autowired(required = false)의 경우 대상 빈이 존재하지 않을 경우 세터 메서드를 호출하지 않는다.
위의 세가지 방식은 메서드 뿐만 아니라 필드에도 동일하게 적용할 수 있다.
생성자 초기화와 필수 여부 지정 방식 동작
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;
}
}
@Autowired(required = false)일 경우
- dateTimeFormatter에 null을 할당하지 않는다.
setDateFormatter()
가 호출되지 않기 때문이다.
@Nullable
- 일치하는 빈이 없을 때 기본 생성자에서 초기화해주고 있어도, dateTimeFormatter에 null 값을 할당한다.
자동 주입과 명시적 의존 주입 간의 관계
'스프링 부트' 카테고리의 다른 글
ApplicationEventPublisher 적용과 그 안에서의 삽질 (1) | 2021.08.14 |
---|---|
스프링 부트 Profile별로 다른 환경 구성 (0) | 2021.08.06 |
📋 3. 스프링 DI (0) | 2021.08.06 |
SpringBoot Auto Configuration (0) | 2021.08.06 |
📋 Spring MVC - 5. 스프링 MVC (0) | 2021.08.06 |