Spring

@Autowired Annotation

김디니 2023. 7. 4. 20:56

@Autowired Annotation란?

스프링에서 지원하는 자동 주입 기능이다.

스프링이 알아서 의존 객체를 찾아서 주입한다.

즉, 스프링이 필요한 의존 빈 객체를 찾아서 주입해준다.

 

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

@Bean
public ChangePasswordService changePwdSvc(){
    ChangePasswordService pwdSvc = new ChangePasswordService();
    // 의존 객체 주입
    pwdSvc.setMemberDao(memberDao());
	return pwdSvc;
}

 

위 코드에서 스프링의 자동 주입 기능을 사용하지 않는다면 pwdSvc.setMemberDao(memberDao()); 코드처럼 직접 의존 객체를 명시해야 한다.

 

하지만, 의존을 주입할 대상에 @Autowired 어노테이션을 붙여준다면  pwdSvc.setMemberDao(memberDao()); 코드를 명시할 필요가 없어진다.

 

@Autowired 를 붙여주기 위해 ChangePasswordService 클래스로 가보자.

 

import....anootation.Autowired;

public class ChangePasswordService{
	
    @Autowired
    private MemberDao memberDao;
    
    public void changePassword(String email, Strin oldPwd, String newPwd) {
    	...
    }
    
    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }
}

 

ChangePasswordService 클래스에서 MemberDao 필드에 @Autowired 를 붙여주었다.

이렇게 어노테이션을 붙여주면 설정 클래스에서 의존을 주입하지 않아도 된다.

@Autowired를 붙여준 이상 스프링이 알아서 해당 타입의 빈 객체를 찾아서 필드에 할당하기 때문이다.

 

그렇다면 AppCtx 클래스의 @Bean 설정 메서드에서 의존을 주입하는 코드를 삭제하자. 

 

@Configuration
public class AppCtx {
	
    @Bean
    public MemberDao memberDao(){
        return new MemberDao();
    }
    
    ...
    
    @Bean
    public ChangePasswordService changePwdSvc(){
        ChangePasswordService pwdSvc = new ChangePasswordService();
        return pwdSvc;
    }
}

 

의존 객체 주입 코드 삭제

 

 

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

 

import ... .annotation.Autowired;

public class MemberInfoPrinter {

    private MemberDao memrDao;
    private MemberPrinter printer;
    
    public void printMemberInfo(String email) {
    	...
    }
    
    @Autowired
    public void setMemberDao(MemberDao memberDao) {
    	this.memrDao = memberDao; // 필드에 할당
    }
    
    @Autowired
    public void setPrinter(MemberPrinter printer) {
    	this.printer = printer; // 필드에 할당
    }
}

 

 

@Autowired를 통해 setMemberDao / setPrinter 메서드에 일치하는 타입인 MemberDao 빈을 스프링 컨테이너에서 찾고 주입해주는 것이다.

setMemberDao / setPrinter 메서드에 주입하는 것이므로 세터 주입 방법을 사용하고 있는 것이다.

 

 

@Autowired와 Bean 매칭

 

각각 @Autowired를 통해 타입이 일치하는 빈 객체를 찾아서 주입하고 있다.

 

 

일치하는 Bean이 없는 경우

위 코드에서 주로 MemberDao의 빈을 찾아서 의존을 주입하고 있다.

만약 MemberDao가 없는 경우 'No qulifying bean of type 'spring.MemberDao' available...' 에러가 발생한다.

적용할 수 았는 MemberDao타입의 빈이 없다는 에러 문구를 띄운 것이다. 

 

만약 일치하는 빈이 두 개 이상이면 'No qulifying bean of type 'spring.MemberPrinter' available: expected single matching bean but found 2...' 라는 에러가 발상한다.

 

이는 어떤 빈인지 정확하게 한정할 수 있어야 하지만 해당 타입의 빈이 두개이기 때문에 어떤 빈을 자동 주입 대상으로 선택해야 할지 한정할 수 없기 때문이다.

 

이를 해결하기 위해서 @Qualifier를 사용한다.

 

 

@Qualifier를 이용한 의존 객체 선택

 

자동 주입이 가능한 빈이 2개 이상일 때 사용한다.

 

 

@Configuration
public class AppCtx {
	
    ...
    
    @Bean
    @Qualifier("printer")
    public MemberPrinter memberPrinter1(){
        return new MemberPrinter();
    }
    
    @Bean
    public MemberPrinter memberPrinter2(){
        return new MemberPrinter();
    }
}

 

memberPrinter1() 메서드에 @Qualifier를 사용하여 "printer" 값을 갖도록 한다.

 

이렇게 지정한 한정값은 @Autowired 에서 자동 주입할 빈을 한정할 때 사용한다.

 

public class MemberListPrinter {

    private MemberDao memrDao;
    private MemberPrinter printer;
    
    
    @Autowired
    @Qualifier("printer")
    public void setMemberPrinter(MemberPrinter printer) {
    	this.printer = printer;
    }

}

 

setMemberPrinter() 메서드는 @Autowired로 인해 MemberPrinter 타입의 빈을 자동 주입하게 된다.

이때 @Qualifier 값이 printer이므로 한정 값이 printer인 빈(memberPrinter1)을 사용하게 된다.

 

 

만약 @Qualifier가 없다면 빈 이름을 한정자로 지정한다.

 

@Configuration
public class AppCtx {
	
    ...
    
    @Bean
    public MemberPrinter printer(){
        return new MemberPrinter();
    }
    
    ...
}

 

그러므로, printer() 메서드의 한정자 이름은 "printer"가 되는 것이다.

 

 

 

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

 

public class MemberSummaryPrinter extends MemberPrinter {
    
    @Overried
    public void print(Member member) {
       ...
    }
}

 

위 코드에서 MemberSummaryPrinter는 MemberPrinter를 상속하고 있다.

 

@Configuration
public class AppCtx {
	
    ...
    
    @Bean
    @Qualifier("printer")
    public MemberPrinter memberPrinter1(){
        return new MemberPrinter();
    }
    
    @Bean
    public MemberSummaryPrinter memberPrinter2(){
        return new MemberSummaryPrinter();
    }
}

 

Application Context 클래스에서 memberPrinter2 빈을 MemberSummaryPrinter 타입으로 변경했다.

하지만, MemberPrinter 타입이 2개이며 @Qualifier를 설정하지 않았을 때의 오류가 발생한다.

 

왜냐하면 MemberSummaryPrinter 클래스가 MemberPrinter를 상속받기 때문이다.

MemberSummaryPrinter 클래스는 MemberPrinter 타입에도 할당할 수 있으므로 memberPrinter1 과 memberPrinter2 중 어떤 빈을 주입해야 하는지 구별할 수 없는 것이다.

그러므로 MemberSummaryPrinter에도 @Qualifier를 설정하여 구별할 수 있도록 한다.

 

 

@Autowired 필수 여부

자동 주입할 대상이 필수가 아닌 경우 3가지의 처리 방법이 있다.

 

1. required 속성을 flase로 지정한다.

 

public class MemberPrinter {

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

}

 

빈이 없어도 exception을 발생하지 않고 자동 주입을 수행하지 않는다.

 

2. Optional

 

public class MemberPrinter {

    private DateTimeFormatter dateTimeFormatter;    
    
    public void print(Member member) {
       ...
    }
    
    @Autowired
    public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
       if(formatterOpt.isPresent()) { // 값이 존재 한다면
    	 this.dateTimeFormatter = formatterOpt.get();   // 할당
       } else {
         this.dateTimeFormatter = null; // null 할당
       }
    }

}

 

자바 8의 Optional을 사용하여 빈이 존재하지 않는다면 Optional을 인자로 전달한다. 

전달 시 exception이 발생하지 않는다.

 

 

3. @Nullable

 

public class MemberPrinter {

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

}

 

@Nullable은 빈이 존재하지 않아도 메서드가 호출된다. 

@Autowired(required = false)는 빈이 존재하지 않으면 메서드가 호출되지 않는다. 

'Spring' 카테고리의 다른 글

스프링 DI  (0) 2023.06.20
싱글톤 Singleton  (0) 2023.06.19
[Spring] DI, IoC  (0) 2023.05.09