나만의 코딩 일지
의존 자동 주입 본문
의존 자동 주입
의존 대상을 설정 코드에서 직접 주입하지 않고 스프링이 자동으로 의존하는 빈 객체를 주입 해주는 기능을 자동 주입이라고 한다.
- 의존 주입을 하지 않을 경우
// 빈 설정
@Configuration
public class Config(){
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public RegisterService registerService(){
return new RegisterService(memberDao()); // 의존 주입
}
}
// 의존 주입
public Class RegisterService(){
MemberDao memberDao;
public RegisterService(MemberDao memberDao){
this.memberDao = memberDao; //의존 주입
}
}
- 의존 주입을 사용했을 경우
// 빈 설정
@Configuration
public class Config(){
@Bean
public MemberDao memberDao(){
return new MemberDao();
}
@Bean
public RegisterService registerService(){
return new RegisterService();
}
}
// 의존 주입
public Class RegisterService(){
@Autowired// 필드에 사용할 경우
MemberDao memberDao;
//--------------------------------------------------
@Autowired // 메서드에 사용할 경우
MemberDao memberDao;
public RegisterService(MemberDao memberDao){
this.memberDao = memberDao;
}
}
@Autowired 어노테이션
- @Autowired 어노테이션을 주입하려는 필드나 메서드에 사용하면 스프링이 자동으로 Context에 등록된 빈을 찾아서 할당해 준다.
- @Autowired 어노테이션 주의할 점
- 일치하는 빈이 없거나 주입 대상에 빈이 두 개 이상일 경우 오류가 발생한다.
// 빈 설정 @Configuration public class Config(){ @Bean public MemberDao memberDao(){ return new MemberDao(); } @Bean public RegisterService registerService1(){ return new RegisterService(); } @Bean public RegisterService registerService2(){ return new RegisterService(); } // 오류 발생 }
@Qualifier 어노테이션을 이용한 의존 객체 선택
자동 주입 가능한 빈이 두 개 이상이면 자동 주입할 빈을 지정할 수 있는 방법이 필요하다. 이때 @Qualifier 어노테이션을 사용하면 자동 주입 대상 빈을 한정할 수 있다.
@Bean
@Qualifier("register")
public RegisterService registerService1(){
return new RegisterService();
}
@Bean
public RegisterService registerService2(){
return new RegisterService();
}
- 위에 코드 처럼 같은 값을 가진 빈이 두 개 이상을 경우 빈에 등록하려는 빈 어노테이션에 @Qualifier 어노테이션을 지정하면 스프링에서 자동으로 @Qualifier 어노테이션을 빈으로 등록한다.
- 기본적으로 빈으로 등록한 빈의 이름은 해당 클래스의 이름으로 등록된다. 막약 @Qualifier 어노테이션을 사용해 빈을 등록한 경우라면 @Qualifier에서 지정한 이름이 빈 이름으로 등록된다.
상위/하위 타입 관계와 자동 주입
public class MemberSummaryPrinter extends MemberPrinter {
@Override
public void print(Member member) {
System.out.printf(
"회원 정보: 이메일=%s, 이름=%s\n",
member.getEmail(), member.getName());
}
}
@Configuration
public class AppCtx {
// ... 생략
@Bean
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
}
위에 코드를 살펴보면 MemberSummaryPrinter 메서드는 MemberPrinter를 상속 받고 있다. AppCtx에서 memberPrinter1, memberPrinter2가 각각MemberPrinter, MemberSummaryPrinter를 빈에 등록하면 익셉션이 발생한다. 왜냐하면 MemberSummaryPrinter는 MemberPrinter 클래스를 상속 받고 있기 때문에 MemberSummaryPrinter는 MemberPrinter에도 할당 받을 수 있기 때문에 스프링에서는 어떤 클래스를 빈에 등록할지 모르기 때문이다.
해결 방법
- @Qualifier 어노테이션 사용
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
return new MemberPrinter();
}
@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
return new MemberSummaryPrinter();
}
각 메서드에 @Qualifier 어노테이션을 사용해 메서드를 지정할 경우 스프링에서 어떤 클래스를 빈으로 등록할지 명확하게 알 수 있기 때문에 익셉션 오류를 피할 수 있다.
- Memberlistprinter가 MemberSummaryPrinter를 사용하도록 수정
public class MemberListPrinter {
private MemberDao memberDao;
private MemberPrinter printer;
// ...
@Autowired
public void setMemberPrinter(MemberSummaryPrinter printer) {
this.printer = printer;
}
MemberListPrinter 메서드에 직접적으로 MemberSummaryPrinter를 주입 받도록 설정하면 된다. MemberSummaryPrinter는 단 하나만 존재하므로 스프링에서 빈으로 등록이 가능하다.
@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;
}
// ...
MemberPrinter 객체는 setDateFormatter 메서드가 null 인지에 여부에 따라 날짜 형식을 바꿔 출력하는 객체이다.
@Autowired 어노테이션는@Autowired 어노테이션이 붙인 타입에 해당하는 빈이 null일 경우(존재하지 않을 경우) 익셉션을 발생한다.
- required = false
- @Autowired가 필수로 주입될 필요가 없을 경우에는 @Autowired(required = false)로 설정하면 익셉션을 발생시키지 않고 자동 주입을 하지 않는다.
- Optional
- 스프링 5부터는 자바 8의 Optional을 사용할 수 있다. ( @Autowired도 null을 처리할 수 있다.)
@Autowired public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) { if (formatterOpt.isPresent()) { this.dateTimeFormatter = formatterOpt.get(); } else { this.dateTimeFormatter = null; } }
- @Nullable 어노테이션
- @Autowired 어노테이션을 붙인 메셔드에서 @Nullable 어노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않을 경우 인자로 null을 전달한다.
@Autowired public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) { this.dateTimeFormatter = dateTimeFormatter; }
required = false와 @Nullable 차이점
@Autowired 어노테이션의 required 속성을 false로 할 때와 차이점은 @Nullable 어노테이션을 사용하면 자동 주입할 빈이 존재하지 않아도 메서드가 호출 된다는 점이다. @Autowired 어노테이션의 경우 required 속성이 false인데 대상 빈이 존재하지 않을 경우 세터 메서드를 호출하지 않는다. 즉 null 값을 전달하지 않는다.
'Spring' 카테고리의 다른 글
SpringTest (0) | 2022.09.11 |
---|---|
AOP 관점 지향 프로그래밍 - Aspect Oriented Programming (0) | 2022.08.26 |
컴포넌트 스캔 (0) | 2022.08.23 |