새소식

인기 검색어

개인공부/SPRING

Spring DI(의존주입) @Autowired (의존 자동주입) 간의 관계

  • -

 

 

의존주입 방법

설정 클래스에서 직접 의존 주입

설정클래스에서 의존객체를 직접 주입해주는 방법이다. 

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

    @Bean
    public MemberRegisterService memberRegisterService(){
        return new MemberRegisterService(memberDao());
    }

    @Bean
    public ChangePasswordService changePasswordService(){
        ChangePasswordService changePasswordService = new ChangePasswordService();
        changePasswordService.setMemberDao(memberDao());
        return changePasswordService;
    }
}

 

 

자동 의존 주입

설정클래스에서 직접 주입하지 않고, 스프링이 자동으로 의존 객체를 주입해주는 방법이다.

 

설정클래스에서 생성자, setter로 memberDao를 주입해주는 코드를 모두 주석처리한다.

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

    @Bean
    public MemberRegisterService memberRegisterService(){
//        return new MemberRegisterService(memberDao());
        return new MemberRegisterService();
    }

    @Bean
    public ChangePasswordService changePasswordService(){
        ChangePasswordService changePasswordService = new ChangePasswordService();
//        changePasswordService.setMemberDao(memberDao());
        return changePasswordService;
    }

}

 

의존을 주입받는 클래스에서 필드 또는 Setter메서드에 @Autowired를 붙인다.

(여기서 생성자에 @Autowired를 붙이면 AppCtx에서 @Bean을 생성할 때 에러가 발생한다. )

public class MemberRegisterService {
    @Autowired	//⭐
    private MemberDao memberDao;

//    constructor DI
//    public MemberRegisterService(MemberDao memberDao) {
//        this.memberDao = memberDao;
//    }

    public Long regist(RegisterDTO req){
        //회원가입 로직
        ...
    }
}
public class ChangePasswordService {
    @Autowired	//⭐
    private MemberDao memberDao;

//    @Autowired //⭐ 여기에 붙여도된다.
//    public void setMemberDao(MemberDao memberDao) {
//        this.memberDao = memberDao;
//    }

    public void changePassword(String email, String oldpwd, String newpwd){
        //비밀번호 변경로직
        ...
    }
}

 

@Autowired 어노테이션을 필드나 setter메서드에 붙이면 

스프링 컨테이너는 타입이 일치하는 Bean객체를 찾아 주입해준다.

 

 

@Autowired 의존 주입 시 타입 일치하는 Bean이 없는 경우

UnsatisfiedDependencyException 발생

 

 

 

 

@Autowired 의존 주입 시 타입 일치하는 Bean이 2개 이상인 경우 -> @Qualifier 어노테이션 사용

@Configuration
public class AppCtx {
    @Bean
    public MemberDao memberDao1(){	//⭐
        return new MemberDao();
    }

    @Bean
    public MemberDao memberDao2(){	//⭐
        return new MemberDao();
    }

    @Bean
    public MemberRegisterService memberRegisterService(){
        return new MemberRegisterService();
    }

    @Bean
    public ChangePasswordService changePasswordService(){
        return new ChangePasswordService();
    }
}
public class MemberRegisterService {
    @Autowired
    private MemberDao memberDao;
    ...
}

public class ChangePasswordService {
    @Autowired
    private MemberDao memberDao;
    ...
}

 

그냥 실행하면 NoUniqueBeanDefinitionException 발생

 

 

 

 

@Qualifier를 사용하여 해결할 수 있다.

@Configuration
public class AppCtx {
    @Bean
    @Qualifier("memberDaoForRegister") //⭐
    public MemberDao memberDao1(){
        return new MemberDao();
    }

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

    @Bean
    public MemberRegisterService memberRegisterService(){
        return new MemberRegisterService();
    }

    @Bean
    public ChangePasswordService changePasswordService(){
        return new ChangePasswordService();
    }
}
public class MemberRegisterService {
    @Autowired
    @Qualifier("memberDaoForRegister") //⭐
    private MemberDao memberDao;

    public Long regist(RegisterDTO req){
        //회원가입 로직
    }
}

 

AppCtx의 @Bean 등록 시 @Qualifier로 이름 지정, @Autowired로 빈 주입 시 @Qualifier로 이름 지정

양쪽에 적어주어서 빈의 이름으로 구분한다.

(동일 타입 빈이 2개일때, 하나에만 Qualifier를 붙여도 두 빈을 구분할 수 있기 때문에 오류없이 실행 가능하다)

 

 

 

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

public class MemberParentPrinter {
}


public class MemberChildPrinter extends MemberParentPrinter{
}

상속관계에서 @Qualifier 애노테이션을 안붙인다면 이것도 NoUniqueBeanDefinitionException이 발생한다.

 

 

 

@Autowired 필수 여부

@Autowired는 기본적으로 타입에 해당하는 빈이 존재하지 않으면 Exception이 발생한다.

 

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

@Autowired
public void setDateFormatter(Optional<DateTimeFormatter> dateTimeFormatter){
	this.dateTimeFormatter = dateTimeFormatter;
}

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

자동 주입할 대상이 필수가 아닌 경우에는

@Autowired 애노테이션의 required 속성을 false로 지정하거나,

Optional을 사용하거나, @Nullable을 사용하여 자동주입을 수행하지 않으면된다.

 

위의 방식은 필드에도 그대로 적용된다.

@Autowired(required = false)
private DateTimeFormatter dateTimeFormatter;

@Autowired
private Optional<DateTimeFormatter> dateTimeFormatter;

@Autowired
@Nullable
private DateTimeFormatter dateTimeFormatter;

 

 

 

생성자 초기화와 필수 여부 지정

public class MemberPrinter{
	private DateTimeFormatter dateTimeFormatter;
    
    public MemberPrinter(){
    	dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
    }
    
    @Autowired(required = false)
    public void setDateTimeFormatter(DateTimeFormatter dateTimeFormatter){
    	this.dateTimeFormatter = dateTimeFormatter;
    }
}

위의 코드를 실행하면 DateTimeFormatter는 기본생성자에서 초기화 된 값("yyyy년 MM월 dd일")을 사용한다.

 => required = false는 매칭되는 빈이 없다면 아무것도 하지 않는다.

 => 기본생성자의 초기화 값을 변경하지 않는다. 

 

 

public class MemberPrinter{
	private DateTimeFormatter dateTimeFormatter;
    
    public MemberPrinter(){
    	dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
    }
    
    @Autowired
    public void setDateTimeFormatter(@Nullable DateTimeFormatter dateTimeFormatter){
    	this.dateTimeFormatter = dateTimeFormatter;
    }
}

위의 코드를 실행하면 DateTimeFormatter는 기본생성자가 아닌 Setter에서 설정된 값을 사용한다.

 => @Nullable은 매칭되는 빈이 없다면 값을 null로 설정한다.

 => 기본생성자의 초기화 값을 설정한 후, Setter를 실행함으로써 null이 설정된다.

 

Optional도 마찬가지로 Optional을 할당한다.

 

 

자동 주입과 명시적 의존 주입

@Configuration
public class AppCtx{

    @Bean @Qualifier("printer")
    public MemberPrinter memberPrinter(){
    	return new MemberPrinter();
    }
    
    @Bean @Qualifier("summaryPrinter")
    public MemberSummaryPrinter memberPrinter2(){
    	return new MemberSummaryPrinter();
    }
    
    @Bean
    public MemberInfoPrinter infoPrinter(){
    	MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
        //MemberInfoPrinter의 setPrinter메서드에는 @Autowired가 붙어있어서
        //memberPrinter(=MemberPrinter)를 설정해준다.
        
        infoPrinter.setPrinter(memberPrinter2());
        //그 다음 setter로 다시 memberPrinter2(=MemberSummaryPrinter)를 설정해준다.
    }
}
public class MemberInfoPrinter{
    @Autowired @Qualifier("printer")
    public void setPrinter(MemberPrinter memberPrinter){
    	this.printer = memberPrinter;
    }
}

 

 

결과는 MemberPrinter의 내용으로 실행된다.

즉, setter DI로 외부에서 값을 넣어주어도

해당 setter에 @Autowired가 붙어있으면 @Autowired가 우선권을 갖는다.

 

우선순위 : @Autowired > Setter DI 주입

 

 

 

정리

• 의존 주입 방법
 - 직접의존주입 : Bean을 생성할 때 setter로 의존을 주입하는 방법
 - 자동의존주입 : Bean을 생성할 때 setter를 사용하지 않고,

                       각 클래스들의 필드/setter에 @Autowired를 붙여서 의존을 주입하는 방법


• @Autowired는 타입이 일치하는 Bean객체를 찾아 주입해준다.
타입 일치 Bean이 없으면 -> UnsatisfiedDependencyException 발생
타입 일치 Bean이 2개 이상이면 -> NoUniqueBeanDefinitionException 발생. 

                                             @Qualifier를 사용하여 각각 빈의 이름을 지정하여 해결 가능

 


• @Qualifier를 사용하면 설정클래스와 주입되는 클래스에 모두 적어주어야한다.

 


• 상위/하위 타입 관계에서 @Qualifier는 안붙이면 -> NoUniqueBeanDefinitionException 발생. @Qualifier로 해결 가능


• 자동 주입할 대상이 필수가 아니면 3가지 방법 사용
 - @Autowired(required = false) : 매칭되는 빈이 없다면 값을 할당하지 않는다
 - Optional : 매칭되는 빈이 없으면 Optional 할당
 - @Nullable : 매칭되는 빈이 없으면 Null을 할당한다.


• 의존 주입받는 클래스의 setter에 @Autowired가 있어서 A를 주입받을 수 있는데
설정클래스에서 setter를 호출해 B를 주입해주면?
@Autowired가 setter DI보다 우선순위가 높기때문에 A로 동작하게된다.

 

 

 

 

 

 

 

'개인공부 > SPRING' 카테고리의 다른 글

Spring MVC 동작원리  (0) 2022.11.01
spring bean scope  (0) 2022.10.26
프로젝트 개발시 참고  (0) 2022.10.19
프로젝트 하면서 알아낸것  (1) 2022.10.18
[Spring] @Controller와 @RestController 차이  (1) 2022.10.15
Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.