본문 바로가기
학습/SPRING

07. 스프링 - 의존 자동 주입

by Elfen Lied 2022. 12. 22.
반응형

06번 글 ( https://simfairy.tistory.com/282 ) 이어서 함

 

- 정리

초기화가 필요한 메서드나 객체들은 생성자 주입이 낫다

chap03 복사
	- 탐색기에서 복사. 폴더이름은 chap03_copy
	- pom.xml : groupId, artifactId 을 chap04로 변경
	- chap03_copy프로젝트 maven import
	- 프로젝트이름변경 chap04
	
1. @Autowired 애너테이션을 이용한 자동 주입
	- ChangePasswordService : MemberDao를 주입 받음
		- setMemberDao메서드에 @Autowired 추가 (세터 자동 주입)
		
	- AppCtx 의존주입설정
		- pwdSvc.setMemberDao(memberDao()); // 삭제
		
	- MemberInfoPrinter
		- setMemberDao, setMemberPrinter 메서드에 @Autowired 추가
		
	- 필드 자동 주입
		- MemberInfoPrinter
		
		- MemberListPrinter
		
		- MemberRegisterService
			- 필드에 @Autowired 추가
			
		- VersionPrinter
			- @Value로 기본타입 값 주입
	
2. 일치하는 빈이 없는 경우
	- AppCtx에서 MemberDao빈 주석 처리
	- 회원가입 테스트 후 예외 확인
		- UnsatisfiedDependencyException 예외 발생
		
		- ChangePasswordService빈을 생성하려면 반드시 MemberDao빈을 주입받아야 하는데
		  MemberDao빈이 존재하지 않아 ChangePasswordService빈을 생성할 수 없음	
		  
		- NoSuchBeanDefinitionException
			- 검색 대상의 스프링빈이 정의되지 않음
			
3. @Autowired 애너테이션을 붙인 주입 대상에 일치하는 빈이 두 개 이상인 경우
	- AppCtx에서 MemberPrinter빈 2개 정의 : memberPrinter1, memberPrinter2
	- NoUniqueBeanDefinitionException 예외 발생
	
4. @Qualifier 애너테이션을 이용한 의존 객체 선택
	- MemberInfoPrinter
	- MemberListPrinter
		- MemberPrinter 필드에 @Qualifier("printer") 추가
		
	- AppCtx
		- MemberInfoPrinter빈 설정 @Qualifier("printer") 추가
		
5. @Autowired required 속성
		- 기본값 : true 의존객체가 억으면 빈을 생성하지 않음
		- false : 의존객체가 없어도 빈을 생성함
		- MemberPrinter 수정

 

 

- MainForSpring 상단 정리

애노테이션 방식만 남기고 나머지 ctx는 삭제나 주석걸기

public class MainForSpring {
	
`
`
`
`
		
		// 애노테이션 방식
		ctx = new AnnotationConfigApplicationContext(AppCtx.class);
        
        
}

 

- @Autowired 애너테이션을 이용한 자동 주입

- AppCtx에서 ChangePasswordService빈에 세터 주입부분을 주석 걸고

package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import spring.ChangePasswordService;
import spring.MemberDao;
import spring.MemberInfoPrinter;
import spring.MemberListPrinter;
import spring.MemberPrinter;
import spring.MemberRegisterService;
import spring.VersionPrinter;

@Configuration
public class AppCtx {
	
	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
	
	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService(memberDao()); // 생성자 주입
	}
	
	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
//		pwdSvc.setMemberDao(memberDao()); // 세터(수정자) 주입
		return pwdSvc;
	}
	
	@Bean
	public MemberPrinter memberPrinter() {
		return new MemberPrinter();
	}
	
	@Bean
	public MemberListPrinter memberListPrinter() {
		// 생성자 주입
		return new MemberListPrinter(memberDao(), memberPrinter());
	}
	
	@Bean
	public MemberInfoPrinter memberInfoPrinter() {
		MemberInfoPrinter memberInfoPrinter = new MemberInfoPrinter();
		
		// 세터(수정자) 주입, Setter DI(Dependency Injection)
		memberInfoPrinter.setMemberDao(memberDao());
		memberInfoPrinter.setPrinter(memberPrinter());
		return memberInfoPrinter;
	}
	
	@Bean
	public VersionPrinter versionPrinter() {
		return new VersionPrinter().setMajorVersion(5).setMinorVersion(0);
	}
}

MainForSpring에서 실행하면 null발생 - memberDao 주입이 되지않기 때문에 null 

 

 

- ChangePasswordService 클래스로 가서

세터 주입 부분에 @Autowired를 붙여주고 메인에서 실행해보자

package spring;

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

public class ChangePasswordService {
	private MemberDao memberDao;

	// 세터(수정자) 주입
	@Autowired
	public void setMemberDao(MemberDao memberDao) {
		this.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); // 업데이트
	}
	
}

 

 

- 필드 자동 주입

- MemberInfoPrinter 

@Autowired 추가

package spring;

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

public class MemberInfoPrinter {
	private MemberDao memberDao;
	private MemberPrinter printer;
	
	@Autowired
	public void setMemberDao(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
	
	@Autowired
	public void setPrinter(MemberPrinter printer) {
		this.printer = printer;
	}
	
	public void printMemberInfo(String email) {
		Member member = memberDao.selectByEmail(email);
		if(member == null) {
			System.out.println("데이터 없음\n");
			return;
		}
		printer.print(member);
		System.out.println();
	}
}

 

 

- AppCtx

MemberInfoPrinter 세터 주입부분 두가지 주석

@Configuration
public class AppCtx {

`
`
`
`
	
	@Bean
	public MemberInfoPrinter memberInfoPrinter() {
		MemberInfoPrinter memberInfoPrinter = new MemberInfoPrinter();
		
		// 세터(수정자) 주입, Setter DI(Dependency Injection)
//		memberInfoPrinter.setMemberDao(memberDao());
//		memberInfoPrinter.setPrinter(memberPrinter());
		return memberInfoPrinter;
	}
	
	@Bean
	public VersionPrinter versionPrinter() {
		return new VersionPrinter().setMajorVersion(5).setMinorVersion(0);
	}
}

메인에서 테스트

 

 

- MemberInfoPrinter

필드에 @Autowired 추가 하고 set 두가지 메서드는 주석

package spring;

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

public class MemberInfoPrinter {
	
	@Autowired
	private MemberDao memberDao;
	
	@Autowired
	private MemberPrinter printer;
	
	
//	public void setMemberDao(MemberDao memberDao) {
//		this.memberDao = memberDao;
//	}
//	
//	
//	public void setPrinter(MemberPrinter printer) {
//		this.printer = printer;
//	}
	
	public void printMemberInfo(String email) {
		Member member = memberDao.selectByEmail(email);
		if(member == null) {
			System.out.println("데이터 없음\n");
			return;
		}
		printer.print(member);
		System.out.println();
	}
}

테스트 실행

 

 

- config 패키지 test01, test02 패키지 삭제

- src/main/resources 안에 test01, test02, appCtx.xml 삭제

 

MainForSpring - ctrl + shift + o 를 눌러 임포트 삭제

 

 

- AppCtx

MemberRegisterService(memberDao);  <-- 괄호안에 memberDao 삭제

빨간줄이 뜰것이다

MemberRegisterService 마우스를 올리고 F3으로 들어간다

package config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import spring.ChangePasswordService;
import spring.MemberDao;
import spring.MemberInfoPrinter;
import spring.MemberListPrinter;
import spring.MemberPrinter;
import spring.MemberRegisterService;
import spring.VersionPrinter;

@Configuration
public class AppCtx {

	`
    `
    `
	
	@Bean
	public MemberRegisterService memberRegSvc() {
		return new MemberRegisterService(); // 생성자 주입
	}
	
	
}

 

 

- MemberRegisterService 클래스

상단에 기본 생성자 하나 생성

필드에 @Autowired 추가

package spring;

import java.time.LocalDateTime;

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

public class MemberRegisterService {

	@Autowired
	private MemberDao memberDao;
	
	public MemberRegisterService() {
		
	}
	
	public MemberRegisterService(MemberDao memberDao) {
		this.memberDao = memberDao;
	}
	
	
	`
    `
    `
    `
}

 

 

 

- AppCtx

return new MemberListPrinter(memberDao(), memberPrinter()); 괄호안에 메소드 삭제

@Configuration
public class AppCtx {

	`
    `
    `
    `
	
		
	@Bean
	public MemberListPrinter memberListPrinter() {
		// 생성자 주입
		return new MemberListPrinter();
	}
	
	`
    `
	`
}

 

 

- MemberListPrinter

기본 생성자 만들고 필드에 @Autowired 추가

package spring;

import java.util.Collection;

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

public class MemberListPrinter {
	
	@Autowired
	private MemberDao memberDao;
	
	@Autowired
	private MemberPrinter printer;
	
	public MemberListPrinter() {
	}
	
	`
    `
    `
}

 

 

- AppCtx

return new VersionPrinter().setMajorVersion(5).setMinorVersion(0); 

뒤쪽 set 부분 다 삭제

@Configuration
public class AppCtx {

`
`
`
	
	@Bean
	public VersionPrinter versionPrinter() {
		return new VersionPrinter();
	}
}

 

 

- VersionPrinter

필드에 @Value로 버전 정보 넣기

package spring;

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

public class VersionPrinter {
	
	@Value("5")
	private int majorVersion;
	
	@Value("0")
	private int minorVersion;
	
	`
    `
    `
    `
	
}

 

 

- 일치하는 빈이 없는 경우

- AppCtx

상단 MemberDao 주석

ChangePasswordService pwdSvc = new ChangePasswordService(); 이부분을

return new ChangePasswordService(); 형태로 변경

@Configuration
public class AppCtx {

//	@Bean
//	public MemberDao memberDao() {
//		return new MemberDao();
//	}
	
	`
    `
    `
	@Bean
	public ChangePasswordService changePwdSvc() {
		return new ChangePasswordService();;
	}
	
	`
    `
    `
}

실행하고 예외 확인하기

UnsatisfiedDependencyException 예외

NoSuchBeanDefinitionException 예외

 

 

- @Autowired 애너테이션을 붙인 주입 대상에 일치하는 빈이 두 개 이상인 경우

- AppCtx에서

다시 MemberDao의 주석을 풀어주고

MemberPrinter 복사해서 두개를 만들어서 각각 1, 2 를 붙여주고 원본은 주석 걸고 메인 실행

@Configuration
public class AppCtx {

	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
	
`
`
`
	
//	@Bean
//	public MemberPrinter memberPrinter() {
//		return new MemberPrinter();
//	}
	
    @Bean
	public MemberPrinter memberPrinter1() {
		return new MemberPrinter();
	}
	
	@Bean
	public MemberPrinter memberPrinter2() {
		return new MemberPrinter();
	}
	
`
`
`
`
}

NoUniqueBeanDefinitionException 예외 확인

 

 

 

- @Qualifier 애너테이션을 이용한 의존 객체 선택

- AppCtx

memberPrinter1상단에 @Qualifier("printer") 붙여줌

@Configuration
public class AppCtx {

	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
	
`
`
`
	
//	@Bean
//	public MemberPrinter memberPrinter() {
//		return new MemberPrinter();
//	}
	
    @Bean
    @Qualifier("printer")
	public MemberPrinter memberPrinter1() {
		return new MemberPrinter();
	}
	
	@Bean
	public MemberPrinter memberPrinter2() {
		return new MemberPrinter();
	}
	
`
`
`
`
}

 

 

 

- MemberListPrinter, MemberInfoPrinter 클래스

필드중 private MemberPrinter printer 상단에 각각 @Qualifier("printer") 붙여준다

public class MemberListPrinter {
	
	@Autowired
	private MemberDao memberDao;
	
	@Autowired
	@Qualifier("printer")
	private MemberPrinter printer;
	
	public MemberListPrinter() {
	}
	
	`
    `
	`
}

 

 

- MemberInfoPrinter

public class MemberInfoPrinter {
	
	@Autowired
	private MemberDao memberDao;
	
	@Autowired
	@Qualifier("printer")
	private MemberPrinter printer;
	
    `
    `
    `
    `
}

 

 

- AppCtx

memberPrinter2 의 이름을 printer로 바꾸고 메인에서 테스트 해본다

@Configuration
public class AppCtx {

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

 

 

 

- spring 패키지 MemberPrinter 클래스 수정

@Autowired(required = false) 붙이고 실행

package spring;

import java.time.format.DateTimeFormatter;

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

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

}

메인에서 테스트 실행

 

반응형

댓글