[Spring5입문] 의존 자동 주입

2021. 8. 4. 17:58CSE/Spring

1. @Autowired 애노테이션을 이용한 의존 자동 주입

- 스프링의 자동 주입 기능을 사용하면 스프링이 알아서 의존 객체를 찾아서 주입한다.

import org.springframework.beans.factory.annotation.Autowired;

public class ChangePasswordService {

	@Autowired //의존을 주입할 대상에 @Autowired 애노테이션 추가
	private MemberDao memberDao;
	
	public void setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
}
//스프링 설정 파일
@Configuration
public class AppCtx{

	@Bean
    public MemberDao memberDao(){
    	return new MemberDao();
    }
    
    @Bean
    public ChangePasswordService changePwdSvc(){
    	Change PasswordService pwdSvc = new ChangePasswordService();
        //pwdSvc.setMemberDao(memberDao(); 를 하지 않아도 자동 주입 됨
       	return pwdSvc;
    }
}

- @Autowired 애노테이션은 메서드에도 붙일 수 있다.

import org.springframework.beans.factory.annotation.Autowired;

public class MemberInfoPrinter {
    private MemberDao memDao;
    private MemberPrinter printer;

    @Autowired
    public void setMemberDao(MemberDao memberDao){
        this.memDao = memberDao;
    }

    @Autowired
    public void setPrinter(MemberPrinter printer){
        this.printer = printer;
    }
}
//스프링 설정 파일
@Configuration
public class AppCtx {

    @Bean
    public MemberDao memberDao(){
        return new MemberDao();
    }

	//생략

    @Bean
    public MemberInfoPrinter infoPrinter(){
        MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
        //infoPrinter.setMemberDao(membeDao());
        //infoPrinter.setPrinter(printer());
        //없이도 자동 주입이 된다.
        return infoPrinter;
    }

}

- 일치하는 빈이 없거나, 일치하는 빈이 두 개 이상이면 에러가 발생한다.

 

2. @Qualifier 애노테이션을 이용한 의존 객체 선택

- 일치하는 빈이 두 개 이상이면 에러가 발생한다.

=> 따라서 자동 주입할 빈을 지정할 수 있어야 한다

=> @Qualifier 애노테이션을 통해 지정할 수 있다.

@Configuration
public class AppCtx{

	//생략
    
    @Bean
    @Qualifier("printer")
    public MemberPrinter memberPrinter(){
    	return new MemberPrinter();
    }
    
    @Bean MemberPrinter memberPrinter2(){
    	return new MemberPrinter();
    }
}
public class MemberListPrinter{
	
    private MemberDao memberDao;
    private MemberPrinter printer;
    
    //생략
    
    @Autowired
    @Qualifier("printer")
    public void setMemberPrinter(MemberPrinter printer){
    	this.printer = printer;
    }
}

 

3. 빈 이름과 기본 한정자

- 빈 설정에 @Qualifier 애노테이션이 없으면 빈 이름이 한정자가 된다.

@Configuration
public class AppCtx{

	@Bean
    public MemberPrinter printer(){
    	return new MemberPrinter();
    }
    //이 경우 한정자가 printer
    
    @Bean
    @Qualifier("mprinter")
    public MemberPrinter printer2(){
    	return new MemberPrinter();
    }
    //이 경우 한정자가 mprinter
}

 

4. 상위/하위 타입 관계와 자동 주입

- MemberPrinter 클래스를 상속하는 MemberSummaryPrinter 클래스를 만들고 이에 대한 빈도 만들자.

public class MemberSummaryPrinter extends MemberPrinter{

    @Override
    public void print(Member member){
        System.out.printf("회원 정보 : 이메일 = %s , 이름 = %s\n", member.getEmail(), member.getName());
    }
}
package config;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import spring.*;

@Configuration
public class AppCtx {

	//생략
    
    @Bean
    public MemberPrinter memberPrinter(){
        return new MemberPrinter();
    }

    @Bean
    public MemberSummaryPrinter memberSummaryPrinter(){
        return new MemberSummaryPrinter();
    }

}

- 이 후, main()을 실행하면 일치하는 빈이 두 개 이상 발견되었을 때와 같은 에러가 발생한다

=> MemberSummaryPrinter 클래스가 MemberPrinter 클래스를 상속해서, MemberSummaryPrinter 클래스는 MemberPrinter 타입에도 할당할 수 있다.

=> 따라서 이 문제는 @Qualifer 애노테이션을 통해서 주입할 빈을 구분해주면 해결할 수 있다.

 

5. @Autowired 애노테이션의 필수 여부

import org.springframework.beans.factory.annotation.Autowired;
import java.time.format.DateTimeFormatter;

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(), member.getRegisterDateTime());
        }
    }
    
    @Autowired
    public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter){
        this.dateTimeFormatter = dateTimeFormatter;
    }
}

- 위의 MemberPrinter 클래스의 print 메소드의 경우, dateTimeFormatter에 의존 객체가 주입되지 않아서 null이어도 제대로 작동한다. 하지만 이 상태로 실행을 하게 되면, 빈이 존재하지 않아서 에러가 발생하게 된다.

=> 이를 해결하는 세 가지 방법이 있다.

 

(1) @Autowired의 required 속성

- @Autowired의 required 속성을 false로 지정하면 매칭되는 빈이 없어도 에러가 발생하지 않고, 자동 주입을 실행하지 않는다.

@Autowired(required = false)
public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter){
this.dateTimeFormatter = dateTimeFormatter;
}

 

(2) 자바 8의 Optional 사용

- 스프링 5 버전 부터는 다음과 같이 Optional을 사용해도 된다.

@Autowired
public void setDateTimeFormatter(Optional<DateTimeFormatter> formatterOpt){
	if (formatterOpt.isPresent(){
    	this.dateTimeFormatter = formatterOpt.get();
    }else{
    	this.dateTimeFormatter = null;
    }
}

 

(3) @Nullable 애노테이션 사용

- @Nullable 애노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 null을 전달한다.

@Autowired
public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter){
  	this.dateTimeFormatter = dateTimeFormatter;
}

 

* Autowired의 required 속성을 false로 할 때와 @Nullable을 사용하는 것은 뭐가 다를까?

=> Nullable을 사용하면 일치하는 빈이 없어도 해당 메소드가 실행된다는 것이다.

 

- 위의 세 가지 방식을 필드에도 그대로 적용할 수 있다.

@Autowired(required = false)
private DateTimeFormatter dateTimeFormatter;
@Autowired
private Optional<DateTimeFormatter> formatterOpt;
@Autowired
@Nullable
private DateTimeFormatter dateTimeFormatter;

 

6. 자동 주입과 명시적 의존 주입 간의 관계

- 만약 @Autowired 애노테이션이 붙여져있는 자동 주입 대상에게 의존을 명시적으로 주입하게 되면 어떻게 될까?

=> 자동 주입을 통해 일치하는 빈을 주입한다.

=> 자동주입 > 명시적 의존 주입