상세 컨텐츠

본문 제목

[스프링2] 4장. 의존 자동 주입

22-23/22-23 Spring 2

by 스머프냠 2022. 10. 13. 10:00

본문

728x90

해당 포스터는 '초보 웹 개발자를 위한 스프링5 프로그래밍 입문' 책을 참고하였다.


4장의 키워드

1. 의존 자동 주입
2. @Autowired 애노테이션
3. @Qualifier 애노테이션
4. @Nullable 애노테이션
5. 명시적 의존 주입


1. 의존 자동 주입

  • 의존 자동 주입 : 스프링이 알아서 의존 객체를 찾아서 주입하는 방법
  • @Autowired 애노테이션, @Resource 애노테이션, @javax.inject.Inject 애노테이션을 사용할 수 있다.
  • 주로 사용하는 것은 @Autowired 애노테이션이다.

 

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

  • @Autowired 애노테이션을 이용하면 Setter 메소드를 사용하지 않아도 스프링이 필요한 의존 빈 객체를 찾아서 주입해준다.

예제1) 자동 주입 기능을 사용하기 전

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

@Bean
public ChangePasswordService changePwdSvc(){
	ChangePasswordServic pwdSvc = new ChangePasswordServic();
    	pwdSvc.setMemberDao(memberDao()); //의존 주입
    	return pwdSvc;
}
  • pwdSvc.setMemberDao(memberDao()); 를 이용하여 의존 주입을 하였다.

예제 2) 자동 주입 기능을 사용한 후

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

@Bean
public ChangePasswordService changePwdSvc(){
	ChangePasswordServic pwdSvc = new ChangePasswordServic(); //자동 의존 주입
    	return pwdSvc;
}
// 의존을 주입하지 않아도 스프링이 @Autowired가 붙인 필드에
// 해당 타입의 빈 객체를 찾아서 주입한다.
  • Setter을 사용하지 않아도 자동으로 의존 주입이 가능하다.

 

방법 1. 의존을 주입할 대상에 @Autowired 애노테이션 붙이기

//자동 의존 주입을 하기 위해
public class ChangePasswordService {

	@Autowired //의존을 부입할 대상에 @Autowired 애노테이션을 붙이면 된다.
	private MemberDao memberDao; // 설정 클래스에서 MemberDao 타입과 일치하는 빈 객체를 찾아서 주입한다.

	public void changePassword(String email, String oldPwd, String newPwd) {
		Member member = memberDao.selectByEmail(email);
		if (member == null)
			throw new MemberNotFoundException();

		member.changePassword(oldPwd, newPwd);

		memberDao.update(member);
	}

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

}
  • memberDao 필드에 @Autowired 애노테이션을 붙인다.
  • 위의 예제 2)와 같이 설정 클래스에서 Setter을 이용하여 의존을 주입하지 않아도 된다.
  • 스프링이 해당 타입(MemberDao)의 빈 객체를 찾아 필드에 해당한다.

 

방법 2. Setter 메서드에 @Autowired 애노테이션 붙이기

// 자동 의존 주입을 하기 위해
public class MemberInfoPrinter {

	private MemberDao memDao;
	private MemberPrinter printer;

	public void printMemberInfo(String email) {
		Member member = memDao.selectByEmail(email);
		if (member == null) {
			System.out.println("데이터 없음\n");
			return;
		}
		printer.print(member);
		System.out.println();
	}

	@Autowired //Setter 메서드에 @Autowired 애노테이션을 붙이면 된다.
	public void setMemberDao(MemberDao memberDao) { // MemberDao 타입과 일치하는 빈 객체를 찾아서 주입된다.
		this.memDao = memberDao;
	}

	@Autowired //Setter 메서드에 @Autowired 애노테이션을 붙이면 된다.
	public void setPrinter(MemberPrinter printer) {// MemberPrinter 타입과 일치하는 빈 객체를 찾아서 주입된다.
    		this.printer = printer;
	}

}
//설정 클래스
public class AppCtx {

	@Bean
	public MemberDao memberDao() { 
	//방법 1의 memberDao 필드와 같은 타입
	//방법 2의 setMemberDao() 메소드 파라미터와 같은 타입
		return new MemberDao();
	}
	
	@Bean
	public MemberyPrinter memberPrinter() { //방법 2의 setPrinter() 메소드 파라미터와 같은 타입
		return new MemberPrinter();
	}

	...생략
    
}

주입이 되는 과정

@Autowired 애노테이션을 필드나 세터 메서드에 붙이면 스프링은 타입이 일치하는 빈 객체를 찾아서 주입한다.

방법1 ) ChangePasswordService 의 memeberDao 필드 타입은 MemberDao이므로 설정클래스(빈 객체)에서
MemberDao 타입을 가진 memberDao 빈이 주입된다.

방법 2) MemberInfoPrinter 클래스의 setPrinter() 메서드의 printer 파라미터 타입이 MemberInfoPrinter 이므로 같은
타입을 가진 설정 클래스의 memberPrinter() 메서드가 실행되면서 printer 빈이 주입된다.


방법 3. 인자 없는 기본 생성자 추가하기 + Setter 메소드에 @Autowired 애노테이션 붙이기

public class MemberListPrinter{
	
	private MemberDao memberDao;
	private MemberPrinter printer;

	public MemberListPrinter(){ //인자가 없는 기본 생성자 추가
    	//Setter에의해 의존 자동 주입 가능
	}

	...생략

	@Autowired //Setter 메서드에 @Autowired 애노테이선 붙이기
	public void setMemberDao(MemberDao memberDao){ //MemberDao 타입과 같은 빈 객체 찾아서 주입 
		this.memberDao = memberDao;
	}

	@Autowired //Setter 메서드에 @Autowired 애노테이선 붙이기
	public void setMemberPrinter(MemberPrinter printer){ //MemberPrinter 타입과 같은 빈 객체 찾아서 주입
		this.printer = printer;
	}
}
@Configuration
public class AppCtx{

	@Bean
	public MemberDao memberDao(){ //setMemberDao() 메소드의 파라미터와 같은 타입
		return new MemberDao();
	}

	@Bean
	public MemberPrinter memberPrinter(){ //setMemberPrinter() 메소드의 파라미터와 같은 타입
		return new MemberPrinter();
	}
	
	..생략
}

 

2-1. 일치하는 빈이 없는 경우

문제 1. @Autowired에 해당하는 빈 객체가 없으면?

  • @Autowired 애노테이션을 붙인 클래스의 필드에 주입할 빈이 존재하지 않아 에러가 발생한다.

 

문제 2. @Autowired 애노테이션을 붙인 주입 대상의 타입이 일치하는 빈이 두개 이상이면?

  • 어떤 빈을 자동 주입 대상으로 선택해야 할지 한정할 수 없다.
@Bean
public MemberPrinter memberPrinter1(){
	return new MemberPrinter();
}

@Bean
public MemberPrinter memberPrinter2(){
	return new MemberPrinter();
}

 

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

  • 자동 주입 대상 빈을 한정할 수 있는 방법
  • @Qualifier("한정값") 방식으로 사용

첫번째 위치) @Bean 애노태이션과 같이

@Configuration
pulbic class AppCtx{

	...생략

	@Bean
	@Qualifier("printer") //해당 빈의 한정값을 "printer"로 지정
	public MemberPrinter memberPrinter1(){
		return new MemberPrinter();
	}

	@Bean
	public MemberPrinter memberPrinter2(){
		return new MemberPrinter();
	}
}
  • 해당 빈의 한정 값으로 "printer"를 지정한다.

두번째 위치) @Autowired 애노테이션과 같이

public class MemberListPrinter{

	private MemberDao memberDao;
	private MemberPrinter printer;

	...생략

	@Autowired
	@Qualifier("printer") //한정 값을 "printer"로 설정
	public void setMemberPrinter(MemberPrinter printer){
		this.printer = printer;
	}
}
  • MemberPrinter에 붙은 @Autowired 애노테이션에 의해 자동 주입을 하러 같은 타입을 가진 빈을 찾으러간다.
  • @Qualifier("printer")에 의해 한정 값이 "printer"인 빈을 의존 주입 후보로 사용한다.
  • memerPrinter1을 자동 주입 대상을 사용한다.

 

3-1. 빈 이름과 기본 한정자

  • @Qualifier 애노테이션이 없을 경우

1. @Bean의 경우

메서드의 이름이 한정자로 설정된다.
ex) printer() 메서드로 정의한 빈의 한정자 : @Qualifier("printer")

2. @Autowired의 경우

클래스의 필드나 메소드의 파라미터의 이름을 한정자로 설정한다.
ex)
필드 - private MemberPrinter printer 의 한정자 : @Qualifier("printer")
메소드의 파라미터 - void setMemberPrinter(MemberPrinter printer) 의 한정자 : @Qualifier("printer")

4. 상위/하위 타입 관계와 자동 주입 - 상속 관계일때

  • MemberPrinter 클래스를 상속한 MemberSummaryPrinter 클래스 관계인 경우
//설정 클래스
@Configuration
public class AppCtx {
	
	@Bean
	public MemberPrinter memberPrinter1() { //MemberPrinter 타입
		retrun new MemberPrinter();
	}

	@Bean
	public MemberSummerPrinter memberPrinter2() { //MemberSummerPrinter 타입
		return new MemberPrinter();
	}
}
  • 상속 관계에서는 한정자를 주지 않으면 익셉션을 발생시킨다.
  • memberPrinter1 타입 빈과 memberPrinter2 타입 빈 중에서 어떤 빈을 주입해야 할지 알 수 없다.

 

해결 방법 1. @Qualifier 애노테이션 사용

//설정 클래스
@Configuration
public class AppCtx {

	...생략

	@Bean
	@Qualifier("printer")
	public MemberPrinter memberPrinter1() {
		return new MemberPrinter();
	}

	@Bean
	@Qualifier("summaryPrinter") //한정자를 summaryPrinter로 설정
	public MemberSummaryPrinter memberPrinter2() {
		return MemberPrinter();
	}
}



//MemberListPrinter 클래스
public class MemberListPrinter {

	private MemberDao memberDao;
    	private Memberprinter printer;

	...생략

	@Autowired
	@Qualifier("summaryPrinter") //한정자를 summaryPrinter로 설정
	public void setMemberPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
}
  • @Qualifier("summaryPrinter)에 의해 memberPrinter2 타입 빈이 주입된다.

 

해결 방법 2. 상속 관계에서 구현체의 타입을 사용

  • 상속 받은 클래스인 MemberSummaryPrinter 타입의 파라미터로 작성한다.
  • 상속자인 MemberPrinter 타입은 MemberPrinter, MemberSummaryPrinter 타입 모두 빈 객체로 주입될 수 있기때문이다.
  • MemberSummartPrinter 타입 빈은 한 개만 존재한다.
public class MemberListPrinter {

	private MemberDao memberDao;
    	private Memberprinter printer;

	...생략

	@Autowired
	public void setMemerPrinter(MemberSummaryPrinter printer) { //타입을 MemberSummaryPrinter로 변경
		this.printer = printer;
	}
}

 

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

  • 자동 주입이 필수가 아닌 경우

 

방법 1. @Autowired(required = false) 사용하기

  • 매칭되는 빈이 없어도 인셉션이 발생하지 않고, 자동 주입을 수행하지 않는다.
//메서드에 사용한 경우
public class MemberPrinter {

	private DateTimeFormatter dateTimeFormatter;

	@Autowired(reqired = false) //주입 대상이 없다면 setDateFormatter() 메소드는 실행하지 않는다.
	public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
		this.dateTimeFormatter = dateTimeFormatter;
	}
}

//필드에 적용한 경우
public class MemberPrinter {

	@Autowired(required=false)
	private DateTimeFormatter dateTimeFormatter;
	
	...생략
}
  • 메서드에 적용한 경우 - 주입 대상이 없다면 setDateFormatter() 메서드를 실행하지 않는다.
  • 필드에 적용한 경우 - 값을 전달하지 않는다.

 

방법 2. Optional 사용하기

  • 형태 : Optional<타입>
  • 빈이 존재하지 않으면 : 값이 없는 Optional을 인자로 전달
  • 빈이 존재하면 : 해당 빈 값을 갖는 Optional을 인자로 전달
//메서드에 사용한 경우
public class MemberPrinter {
	private DateTimeFormatter dateTimeFormatter;

	...생략
	
	@Autowired
	public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
		if (formatterOpt.isPresent()) { //값이 존재한다면
			this.dateTimeFormatter = formatterOpt.get(); //DateTimeFormatter 타입 빈을 주입받아 필드에 할당한다.
		} else { //값이 존재하지 않으면
			this.dateTimeFormatter = null; //dateTimeFormatter에 null을 할당한다.
		}
	}
    
//필드에 사용한 경우
public class MemverPrinter {
	
	@Autowired
	private Optional<DateTimeFormatter> formatterOpt;

	...생략

}

 

방법 3. @Nullable 애노테이션 사용하기

  • 빈이 존재하지 않으면 : null을 전달
  • 빈이 존재하면 : 해당 빈을 인자로 전달
//메서드에 사용한 경우
public class MemberPrinter {
	private DateTimeFormatter dateTimeFormatter;

	...생략

	@Autowired
	public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) { //@Nullable 애노테이션 붙이기
		this.dateTimeFormatter = dateTimeFormatter;
	}
}

//필드에 사용한 경우
public class MemberPrinter {

	@Autowired
	@Nullable
	private DateTimeFormatter dateTimeFormatter;

	...생략
}


※ @Autowired(required = false)와 @Nullable 애노테이션의 차이점

  • @Nullable의 경우 빈이 존재하지 않아도 메서드가 호출된다.
  • @Autowired의 경우 빈이 존재하지 않으면 메서드가 호출되지 않는다.

 

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

  • 우선 순위 : @Autowired 애노테이션을 통한 의존 주입 > Setter 메서드를 통한 의존 주입
  • Setter 메서드를 이용하기보다 스프링이 제공하는 자동 주입 기능을 사용하는 것이 좋다.

 


Quiz.

빈칸, OX 문제
1. @Autowired 애노테이션을 이용해 의존 자동 주입을 할 수 있는데, 방식이 두가지이다. 첫번째는 (의존 주입할 대상)에 붙일 수 있고, 두번째는 (메서드)에 붙일 수 있다.
2. 자동 주입 가능한 빈이 두개 이상이면 (@Qualifier) 애노테이션을 이용하여 자동 주입 대상 빈을 한정할 수있다.
3. @Qualifier 애노테이션이 없으면 @Bean 애노테이션의 경우 (빈 이름)을 한정자로 지정하고, @Autowired 애노테이션의 경우 (필드)나 (파라미터 이름)을 한정자로 사용한다.
4. @Autowired(required=false)를 사용하였을때, 빈이 존재하지 않으면 메서드가 호출된다. (O, X)
답 : X
5. @Nullable 애노테이션과 Optional을 사용하는 방식은 빈이 존재하지 않아도 null을 인자로 받아 모두 메서드가 호출된다. (O, X)
답: O
6. 의존 주입의 우선 순위는 (@Autowired 애노테이션)을 사용하는 것이 먼저 사용된다.
7. @Autowired 애노테이션을 필드나 메서드에 붙이면 스프링은 (타입)이 일치하는 빈 객체를 찾아서 주입한다.

주관식 퀴즈
1. 아래 코드를 보고 자동으로 생성되는 @Qualifier 한정자를 쓰시오.


1번. 빈객체의 메소드
2번. 클래스의 필드

3번. 클래스의 메소드

//1.

@Bean
public MemberPrinter printer2() { //어떤 한정자?
	return new MemberPrinter();  
}

//2.

public class MeberListPrinter {
	
	private MemberDao memberDao; 
	private MemberPrinter printer; //어떤 한정자?
	
	@Autowired
	public void setMemberPrinter(MemberPrinter printer){ //어떤 한정자?
		this.printer = printer;
	}
}

정답 : 1번: @Qualifier("printer2"), 2번: @Qualifier("memberDao"), 3번: @Qualifier("printer")

 

2. @Autowired가 필수가 아닐때 사용할 수 있는 3가지 방법 중 하나를 이용하여 코드를 고쳐보세요.

public class MemberListPrinter {
	
	...생략
	
	@Autowired
	public void setMemberPrinter(MemberSummaryPrinter printer) {
		this.printer = printer
	}
}

1) @Autowired(requred=false) 사용
2) optional 사용

3) @Nullable 사용


Corner Spring #2

Editor : 스머프

728x90

관련글 더보기