Back-end/Spring-핵심& webMVC

ch4. 의존자동주입(#스프링5 프로그래밍 입문-최범균 저)

prden 2021. 5. 6. 21:51

1. 설정 클래스에서 빈의 모습(직접 의존 주입했을 경우와 하지 않았을 경우)

설정 클래스에 빈으로만 등록해주고 의존 주입을 직접 set이나 생성자로 해주지 않아도 @Autowired를 통해 자동 의존 주입을 가능하게 할 수 있다.  ※Component-scan 기능을 통해 설정 클래스에 빈으로 등록하지 않아도 원하는 클래스를 빈으로 등록할 수 있다.

@Configuration
public class AppCtx {

	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
	
	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService();
	}
	//자동 주입 기능을 사용하면 이렇게 의존을 주입하지 않아도 됨 set~~
	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
		pwdSvc.setMemberDao(memberDao());
		return pwdSvc;
	}
    //이렇게 빈으로만 등록해놓고 @Autowired붙이면 된다.
        @Bean
	public ChangePasswordService changePwdSvc() {
		return new ChangePasswordService();
	}

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

@Autowired애노테이션은 필드에다 붙여도 되고 생성자, set메서드에 붙여도 된다.

아래 코드 블록은 set메서드에 @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
	public void setMemberDao(MemberDao memberDao) {
		this.memDao = memberDao;
	}

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

}

 

아래 코드 블록은 필드에 @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);
	}

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

}

아래 코드 블록은 생성자를 통해 자동 의존 주입을 위해 필드에 @Autowired를 붙이고 기본 생성자와 MemberDao를 인자로 가지는 생성자를 추가해준 모습이다. 기본 생성자를 생성해준 이유는 AppCtx클래스에서 기본 생성자를 통해 객체를 생성하기 위함이다. 

public class MemberRegisterService {
	@Autowired
	private MemberDao memberDao;

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

	public Long regist(RegisterRequest req) {
		Member member = memberDao.selectByEmail(req.getEmail());
		if (member != null) {
			throw new DuplicateMemberException("dup email " + req.getEmail());
		}
		Member newMember = new Member(
				req.getEmail(), req.getPassword(), req.getName(), 
				LocalDateTime.now());
		memberDao.insert(newMember);
		return newMember.getId();
	}
}

※@Autowired가 생략 가능한 경우(스프링 4.3부터 가능)

public class MemberRegisterService {
	
	private MemberDao memberDao;

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

	public Long regist(RegisterRequest req) {
		Member member = memberDao.selectByEmail(req.getEmail());
		if (member != null) {
			throw new DuplicateMemberException("dup email " + req.getEmail());
		}
		Member newMember = new Member(
				req.getEmail(), req.getPassword(), req.getName(), 
				LocalDateTime.now());
		memberDao.insert(newMember);
		return newMember.getId();
	}
}

MemberDao가 빈으로 등록되어 있고 MemberRegisterService(MemberDao memberDao)같이 생성자가 하나 있을 경우 위와 같이 @Autowired 생략되어 있어도 의존 자동 주입 가능하다. ** 그러나 상호 참조 관계에서 이렇게 생성자로 만들면 문제가 발생할 수 있다.(교착상태에 빠짐)

또한, 아래와 같이 생성자를 없애도 의존 자동 주입이 가능하다. 

public class MemberRegisterService {
	
    @Autowired
	private MemberDao memberDao;

	public Long regist(RegisterRequest req) {
		Member member = memberDao.selectByEmail(req.getEmail());
		if (member != null) {
			throw new DuplicateMemberException("dup email " + req.getEmail());
		}
		Member newMember = new Member(
				req.getEmail(), req.getPassword(), req.getName(), 
				LocalDateTime.now());
		memberDao.insert(newMember);
		return newMember.getId();
	}
}

3. @Autowired(required=false), Optional, @Nullable의 관계

4. 자동 주입과 명시적 의존 주입(수동)이 동시에 설정되어있다면?

설정 클래스에서 세터 메서드를 통해 의존을 주입해도 해당 세터 메서드에 @Autowired애노테이션이 붙어 있으면 자동 주입을 통해 일치하는 빈을 주입한다.

**자동 주입을 하는 코드와 수동으로 주입하는 코드가 섞여 있으면 주입을 제대로 하지 않아 NullPointerException이 발생할 때 원인을 찾기 힘들 수 있으니 의존 자동 주입을 사용하고 있으면 일부 자동 주입을 적용하기 어려운 경우를 제외하고는 일관적으로 의존 자동 주입을 사용하는 것이 좋다.