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

2021. 7. 12. 14:15JAVA/Spring

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

 

목적

본 글의 목적은 상위 클래스와 하위 클래스를 정의하고 해당 타입의 빈 설정 메소드를 같은 스프링 설정 클래스 안에서 정의한다. 그리고 스프링이 자동 의존 주입 대상 선택시 예외가 발생하는지 확인한다.

 

우선 상위/하위 클래스를 정의하기 위해 MemberPrinter 클래스와 MemberSummary 클래스를 정의한다.

MemberPrinter 클래스 정의

public class MemberPrinter {
	
	public void print(Member member)
	{
		System.out.printf("회원 정보 : 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n",
							member.getId(), member.getEmail(), member.getName(), member.getRegisterDateTime());
	}
}

 

MemberSummaryPrinter 클래스 정의

public class MemberSummaryPrinter extends MemberPrinter{

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

MemberSummaryPrinter 클래스는 MemberPrinter 클래스의 하위 클래스임을 확인할 수 있다.

 

스프링 설정 클래스(AppCtx 클래스) 정의

@Configuration
public class AppCtx{

	... 생략

	// 특이점
	// memberPrinter1 빈 설정 메소드와 memberPrinter2 빈 설정 메서드가
	// 리턴의 이름은 다르지만 MemberSummaryPrinter는 MemberPrinter 클래스의
	// 하위 클래스 이므로 자동 의존 주입 과정에서 중복을 일으키는 문제가 발생한다. 
	@Bean
	public MemberPrinter memberPrinter1()
	{
		return new MemberPrinter();
	}
	
	@Bean
	public MemberSummaryPrinter memberPrinter2()
	{
		return new MemberSummaryPrinter();
	}
}

MemberInfoPrinter 클래스 정의

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();
	}
	
	// 특이점
	// @Qualifier 애노테이션 삭제
	@Autowired
	public void setMemDao(MemberDao memDao) {
		this.memDao = memDao;
	}
	@Autowired	
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
	
	
}

MemberListPrinter 클래스 정의

import java.util.Collection;

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

import chap04_04.spring.Member;
import chap04_04.spring.MemberDao;
import chap04_04.spring.MemberPrinter;


public class MemberListPrinter {
	private MemberDao memberDao;
	private MemberPrinter printer;
	
	public MemberListPrinter()
	{
		
	}
	
	public MemberListPrinter(MemberDao memberDao, MemberPrinter printer) 
	{
		this.memberDao = memberDao;
		this.printer = printer;
	}
	
	public void printAll() 
	{
		Collection<Member> members = memberDao.selectAll();
		members.forEach(m->printer.print(m));
	}

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

	// 특이점
	// @Qualifier 애노테이션 삭제
	@Autowired
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
}

 

MainForSpring 클래스 정의

https://github.com/yonghwankim-dev/spring5/blob/master/sp5-chap04/src/main/java/chap04_04/main/before/MainForSpring.java

 

yonghwankim-dev/spring5

최범균 저, 초보 웹 개발자를 위한 스프링5 프로그래밍 입문 예제. Contribute to yonghwankim-dev/spring5 development by creating an account on GitHub.

github.com

 

실행결과

결과분석

상위 클래스와 하위 클래스로 정의했더라도 스프링 컨테이너는 @Qualifier 애노테이션 없이 빈 설정 메서드를 한정지을 수 없다.

상위 클래스, 하위 클래스 이름으로 따로 빈 설정 메소드로 정의했음에도 불구하고 예외가 발생하는 이유는 MemberSummaryPrinter 클래스가 MemberPrinter 클래스를 상속했기 때문이다. MemberSummaryPrinter 클래스는 MemberPrinter 타입에도 할당 할 수 있으므로, 스프링 컨테이너는 MemberPrinter 타입 빈을 자동 주입해야 하는 @Autowired 애노테이션 태그를 만나면 memberPrinter1 빈과 memberPrinter2 타입 빈 중에서 어떤 빈을 주입해야 할지 알 수 없다. 그래서 예외를 발생시키는 것이다.

 

 

상위/하위 타입 관계에서 @Qualifier 애노테이션을 통한 자동 주입 수행

MemberListPrinter 클래스와 MemberInfoPrinter 클래스가 MemberPrinter 타입의 빈을 자동 주입하므로 어떤 빈을 주입할지 결정해야 한다.

 

스프링 설정 클래스에 @Qualifier 애노테이션 적용 (printer)

@Configuration
public class AppCtx{

	... 생략
	
	// 특이점
	// @Qualifier 애노테이션 적용, 한정자 값 "printer"로 설정
	@Bean
	@Qualifier("printer")
	public MemberPrinter memberPrinter1()
	{
		return new MemberPrinter();
	}

	// 특이점
	// @Qualifier 애노테이션 적용, 한정자 값 "summaryPrinter"로 설정
	@Bean
	@Qualifier("summaryPrinter")
	public MemberSummaryPrinter memberPrinter2()
	{
		return new MemberSummaryPrinter();
	}
}

MemberInfoPrinter 클래스에 @Qualifier 애노테이션 적용

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
	public void setMemDao(MemberDao memDao) {
		this.memDao = memDao;
	}
	// 특이점
	// @Qualifier 애노테이션 적용, 스프링이 자동 주입시 memberPrinter1() 메소드로 한정하게됨
	@Autowired	
	@Qualifier("printer")
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
	
	
}

 

MemberListPrinter 클래스에 @Qualifier 애노테이션 적용 (summaryPrinter)

public class MemberListPrinter {
	private MemberDao memberDao;
	private MemberPrinter printer;
	
	public MemberListPrinter()
	{
		
	}
	
	public MemberListPrinter(MemberDao memberDao, MemberPrinter printer) 
	{
		this.memberDao = memberDao;
		this.printer = printer;
	}
	
	public void printAll() 
	{
		Collection<Member> members = memberDao.selectAll();
		members.forEach(m->printer.print(m));
	}

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

	// 특이점
	// @Qualifier 애노테이션 적용, 스프링이 자동 주입시 memberPrinter2() 메소드로 한정하게됨
	@Autowired
	@Qualifier("summaryPrinter")
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
}

MainForSpring 클래스 정의

https://github.com/yonghwankim-dev/spring5/blob/master/sp5-chap04/src/main/java/chap04_04/main/after/MainForSpring.java

 

yonghwankim-dev/spring5

최범균 저, 초보 웹 개발자를 위한 스프링5 프로그래밍 입문 예제. Contribute to yonghwankim-dev/spring5 development by creating an account on GitHub.

github.com

실행결과

결과분석

위 결과를 통해서 info 명령어 수행결과 MemberInfoPrinter 클래스의 자동 주입이 memberPrinter1() 메서드를 통해서 주입된 것을 알 수 있다. 그리고 list 명령어 수행결과 MemberListPrinter 클래스의 자동 주입이 memberPrinter2() 메서드를 통해서 주입된 것을 알 수 있다.

 

정리하며

상위/하위 클래스 타입의 빈 메소드를 작성하여도 @Qualifier 애노테이션을 따로 지정하지 않으면 스프링은 빈을 한정지을 수 없다. 이유는 하위 클래스는 상위 클래스에 할당이 가능하기 때문이다. 따라서 각각의 빈 메소드에 @Qualifier 애노테이션을 지정하면 상위/하위 클래스를 작성하여도 자동 주입할 빈 메소드를 한정 지을 수 있다.

 

References

초보 웹 개발자를 위한 스프링5 프로그래밍 입문, 최범균 저
https://github.com/yonghwankim-dev/spring5