[Spring] 자동 주입

2022. 8. 31. 14:51Spring

728x90
반응형
SMALL

@Autowired

자동의존주입받을 대상(필드, 세터 메소드)에 @Autowired 애노테이션을 붙이면

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

package spring;

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

public class ChangePasswordService {

	@Autowired //자동 주입 기능
	private 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);
	}
	
	//자동 주입이 되므로 setMemberDao 메소드는 사용X
	public void setMemberDao(MemberDao memberDao) { //의존 객체 주입
		this.memberDao=memberDao;
	}
}

위의 코드에서 memberDao 변수는 자동주입된다.

MemberDao 타입의 빈 객체가 존재한다면 해당 빈 객체가 memberDao에 주입되므로 memberDao의 세터는 사용하지 않아도 된다.

 

 

만약 대응하는 빈 객체가 존재하지 않는다면 어떻게 될까?

해당 타입의 빈 객체가 존재하지 않는다는 내용의 에러가 발생한다.

 

같은 타입의 빈 객체가 2개이상이면 어떨까?

마찬가지로 어떤 빈인지 한정할 수 없다는 내용의 에러가 발생한다.


@Qualifier

자동 주입 가능한 빈이 2개 이상일 때 빈을 지정할 수 있는 방법이 있다.

@Qualifier 애노테이션을 이용해 의존 객체를 선택하는 것이다.

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

위와 같이 같은 타입의 빈 객체가 2개있다.

@Qualifier 애노테이션을 붙이고 그 뒤에 각각 "printer" 와 "print" 값을 한정 값으로 지정했다.

	@Autowired
	@Qualifier("print")
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
    
        @Autowired
	@Qualifier("printer")
	public void setMemberPrinter(MemberPrinter printer) {
		this.printer=printer;
	}

실제로 자동주입할 때 주입할 대상에 @Qualifier 애노테이션과 한정 값을 붙이면 해당 빈 객체가 주입된다.

(함정은 자동주입하는 모든 대상에 한정값을 붙여야 에러가 나지 않는 것 같다.)

 

!!!몇 가지 실험하면서 새롭게 알게 된 사실!!!

한정 값은 String 타입의 const expression 이어야 한다.

그래서 int형 같은 다른 타입은 안되고 큰 따옴표로 표현하거나 String 타입의 변수로 지정가능한데,

만일 String 변수로 지정할 경우 final을 붙여야 한다.

왜냐하면 위에서도 언급했듯이 const expression 이어야하기 때문이다.

(근데 굳이 final String으로 할 일이 있을까? 프로젝트를 해봐야 알 듯)


빈 이름과 기본 한정자

@Qualifier 애노테이션이 없으면 빈의 이름을 한정자(한정값?)로 지정한다.

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

같은 타입의 두 빈 객체가 있다. 이 때 빈 이름은 한정자가 된다.

 

@Autowired
private MemberDao memberDao;
@Autowired
private MemberDao memberDao1;

필드 이름을 한정자로 사용하면 빈 객체를 한정하여 주입할 수 있다.


상속 관계에서의 자동 주입

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

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

MemberSummaryPrinter 클래스가 MemberPrinter 클래스를 상속하고 있다.

어떤 대상에게 MemberPrinter 타입의 빈 객체를 자동주입하려고 하면

한정자없이는 오류가 난다.

왜냐하면 MemberPrinter 타입은 MemberSummaryPrinter 타입의 객체를 받을 수 있기 때문이다.

 

MemberPrinter 타입을 주입받고 싶다면 위와 같이 지정된 한정자를 사용하고,

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;
	}
}

위 코드에서 dateTitmeFormatter는 반드시 필요한 필드는 아니다.

null이어도 해당 클래스의 기능이 잘 구현되기 때문이다.

하지만 이대로 실행하면 DateTimeFormatter 빈 객체가 없을 경우 에러가 발생한다.

 

이런 상황을 해결하는 방법에는 3가지가 있다.

 

첫 번째는 required 속성을 활용하는 것이다.

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

@Autowired 애노테이션 옆에 required 속성을 false로 지정하면

주입할 타입의 빈 객체가 없을 경우 해당 메소드를 실행하지 않는다.

 

 

두 번째는 자바8의 Optional을 사용하는 것이다.(spring 5부터 사용가능)

	@Autowired
	//해당 타입의 빈 객체가 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달
	//존재하지 않으면 값이 없는 Optional을 인자로 전달
	public void setDateFormatter2(Optional<DateTimeFormatter> formatterOpt) {
		if(formatterOpt.isPresent())
			this.dateTimeFormatter=formatterOpt.get();
		else
			this.dateTimeFormatter=null;
	}

주입할 타입의 빈 객체가 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달하고,
존재하지 않으면 값이 없는 Optional을 인자로 전달한다.

 

 

세 번째는 @Nullable 애노테이션을 사용하는 것이다.

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

자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 null을 인자로 전달한다.

 

 

여기서 required 와 @Nullable 의 차이점은 주입할 빈이 존재하지 않을 경우

required는 메소드가 호출되고, @Nullable은 메소드가 호출되지 않는다는 점이다.

 

그리고 다음 코드를 보자.

public class MemberPrinter {
	private DateTimeFormatter dateTimeFormatter;
	
    public MemberPrinter() {
		dateTimeFormatter=DateTimeFormatter.ofPattern("yyyy년 MM월 dd일");
	}
    
	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(required=false)
	public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
		this.dateTimeFormatter=dateTimeFormatter;
	}
}

생성자에서 dateTimeFormatter를 초기화한다.

required 로 자동주입할 경우 일치하는 빈이 존재하지 않을 때 주입 대상인 필드나 메소드에 null을 전달하지 않는다.

위 코드의 실행결과는 초기화 형식대로 출력된다.

하지만 @Nullable 애노테이션을 사용하면 null값을 전달하기 때문에 초기화하지 않은 형식으로 출력된다.


명시적 의존 주입

	@Bean
	public MemberInfoPrinter infoPrinter() {
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		infoPrinter.setPrinter(memberPrinter2());
		return infoPrinter;
	}

위의 빈 객체는 명시적으로 memberPrinter1이 아닌 memberPrinter2를 주입하고 있다.

하지만 주입받는 대상이 @Autowired 를 통해 memberPrinter1을 자동 주입받고 있다면

결국은 memberPrinter1이 자동 주입된다.

 

따라서 @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 주입하기보다는

일관되게 자동주입기능을 사용하는 것이 편리하다.

728x90
반응형
LIST

'Spring' 카테고리의 다른 글

[Spring] AOP 프로그래밍  (0) 2022.09.05
[Spring] 빈 라이프사이클과 범위  (0) 2022.09.04
[Spring] 컴포넌트 스캔  (0) 2022.09.04