ch03. 스프링 DI

728x90
참고 도서 : 스프링5 프로그래밍 입문 - 최범균 저

ch03. 스프링 DI

<객체 의존과 의존 주입(DI) > 

-의존 : 객체 간 의존
-의존 주입 : 의존하는 대상을 주입하는 것
             //결국 객체 의존 위해서 의존 주입을 하는 것

#[객체 의존]

: 한 클래스 내부에서 다른 클래스 객체를 생성 후 -> 그 클래스의 메소드를 활용하는 것

import java.time.LocalDateTime;

public class MemberRegisterService { //회원 가입 처리 클래스 
	//필드
	private MemberDao memberDao = new MemberDao(); //(1) 직접 의존 객체 생성 
	
	//메소드 
	public void regist(RegisterRequest req) { //회원 등록 메소드 
		//이메일로 회원 데이터 조회 
		Member member = memberDao.selectByEmail(req.getEmail());
		
		if(member != null) { //기존 데이터에 이미 존재하는 이메일이면 예외처리 
			throw new DuplicateMemberException("dup email"+ req.getEmail());
		}
		
		//기존 데이터에 존재하지 않는 이메일이면 DB에 삽입 
		Member newMember = new Member(req.getMail(),req.getPassword(),req.getName(),LocalDateTile.now());
		memberDao.insert(newMember); 
	}
}
-이 클래스 내부에서는 DB 처리를 위해
(외부의) MemberDao클래스를 new 연산자로 직접 생성한 뒤, 해당 클래스의 메소드를 사용즉, 객체 의존 상태에 있다.
이 경우, 기존 클래스 객체 생성 시 -> 내부에서 사용된 의존 객체도 함께 생성된다.

#[의존 주입]

   <의존 객체 주입> : 서로 다른 두 객체를 조립하는 것

 (1) 클래스 내부에서 직접 의존 객체 생성 : new 연산자로 직접 생성하는 방법

- 재사용 X, 코드 변경 시 불편함

 (2) DI 방식 이용하여 의존 객체 생성 : 의존 객체를 전달받는 방법

- 재사용 O, 코드 변경 유연함

① 클래스 내부에 생성자(의존 객체) 인수로 넣어서 선언

: 
생성자를 통해 의존 객체를 주입 받는 것

: 기존 클래스의 객체 생성 시
인수로 의존 객체를 넣어서 생성
 

② 클래스 내부에서 set_(의존 객체) 인수로 넣어서 선언



: Setter()
메소드 통해 의존 객체를 주입 받는 것이다.




[DI 장점 : 의존 객체 변경의 유연함]

   (1) 직접 의존 객체 생성할 경우

  -> 의존 객체 변경 시, 번거롭게 직접 new로 생성해줬던 의존 객체 모두 고쳐주어야 함

   (2) DI방식 이용하여 의존 객체 생성

-> 의존 객체 변경 시, 생성자/Setter 인수로 넘겨주는 의존 객체 타입만 변경

-> , 의존 객체 주입하는 코드 한 부분만 변경

//DI 의존 주입 코드
MemberDao memberDao = new MemberDao(); //의존 객체 생성  -> 이 부분만 수정하면 됨
MemberRegisterService regSvc = new MemberRegisterService(memberDao); 
						//기존 객체 생성자에 의존객체 주입 
ChangePasswordService pwdSvc = new ChangePasswordService(memberDao); 
						//기존 객체 생성자에 의존객체 주입

[객체 조립기]

-의존 객체 주입해주는 코드만 모아놓은 클래스

-의존 객체 생성 후 기존 객체에 의존 객체 주입 기능 제공 

        *의존, DI, 조립기에 대해 먼저 알아본 이유는 ‘스프링’이 DI를 지원하는 조립기이기 때문 !!!!!!!!!!!!!!!!!!!!1


...! Spring 진짜 시작 ...!

 [스프링의 DI 설정] : [ 스프링 설정 클래스 작성 -> 스프링 컨테이너 생성 -> getBean() ]

- 스프링 = DI를 지원하는 범용 객체 조립기
         (사용할 객체를 빈 객체로 등록해서 의존 객체로서 사용)

스프링 설정 클래스 생성: 설정 정보작성 | @Configuration

: 스프링이 어떤 객체를 생성하고 의존 어떻게 주입할지 정의한 정보
: 이 클래스 내부에서 @Bean 애노테이션을 토대로
해당 메소드가 생성한 객체를 스프링 빈 객체로 설정한다. , 사용할 의존객체를 빈 객체로 설정

스프링 컨테이너 생성 : 설정 클래스를 이용 -> 스프링 컨테이너 생성

: 설정 정보를 토대로 실질적으로 객체 생성하고 의존 객체 주입하는 역할은 스프링 컨테이너가 하므로, AnnotationConfigApplicationContext 클래스로 스프링 컨테이너 생성
//이 클래스 객체 생성자 인수로 설정클래스를 넣어준다.
//설정 파일로부터 생성할 객체와 의존 주입 대상을 정한다.

getBean() 이용

> 사용할 빈 객체 가져옴
: 위에서 생성했던 스프링 컨테이너 속에서 getBean() 하여 빈 객체를 구함

[스프링의 DI 방식]

  ① 생성자 DI 방식

: 빈 객체 생성 시점에 모든 의존 객체가 주입된다.

- 스프링 설정 클래스에서 생성자를 이용하여 의존 객체를 주입하기 위한 DI 설정해둔 @Bean 메소드 대상으로                                                                                     getBean() 호출 시-> 빈 객체 생성 시점에 의존 객체 주입됨

@Bean
public MemberRegisterService memberRegSvc() {
	return new MemberRegisterService(memberDao()); //생성자 인수에 의존 객체 주입 설정
}
 

- 의존 객체 2개 이상인 경우에도 동일하게 주입 설정 코드 작성하면 됨

@Bean
public MemberListPrinter listPrinter() {
	return new MemberListPrinter(memberDao(), memberPrinter()); 
					//생성자 인수에 의존 객체 주입 설정 
}

 

  ② Setter 메소드 DI 방식

: 세터 메서드 이름을 통해 주입되는 의존 객체 정보 집작 가능

                                                                               -자바빈 규칙: Stter메소드 파라미터 1

//Setter() 이용 -> 의존 객체 주입받는 코드 
public void setMemberDao(MemberDao memberDao) {
	this.memDao - memberDao;
}
//Setter() 이용 -> 의존 주입 설정 코드 
@Bean
public MemberBerInfoPrinter infoPrinter() {
	MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
	infoPrinter.setMemberDao(memberDao()); 
	return infoPrinter;
}

[빈으로 등록한 객체의 기본 데이터 타입 값 설정

두 개의 int 값 필드를 갖던 클래스를 빈 객체로 설정할 때 값 설정 가능

자바빈에서는 Getter,Setter를 이용해서 프로퍼티를 정의한다.
//빈 등록할 객체 
public class VersionPrinter {
	//필드
	private int majorVersion;
	private int minorVersion;
	//...
	
	//Setter()
	public setMajorVersion(int majorVersion) {
		this.majorVersion = majorVersion;
	}
	public setMinorVersion(int minorVersion) {
		this.minorVersion = minorVersion;
	}
}
---------------------------------------------------------------------
//스프링 설정 클래스 
@Configuration
public class AppCtx {
	...
	
	@Bean
	public VersionPrinter versionPrinter() {
		VersionPrinter versionPrinter = new VersionPrinter();
		versionPrinter.setMajorVersion(5); //빈 객체의 기본 데이터값 설정 
		versionPrinter.setMinorVersion(0); //빈 객체의 기본 데이터값 설정 
		return versionPrinter;
	}
}

[스프링과 싱글톤]

-스프링 컨테이너가 생성한 빈은 싱글톤 객체
-스프링 컨테이너는 @Bean 메소드에 대해 한 개의 객체만 생성한다.
-, 다른 설정 메소드에서 동일한 @Bean 메소드를 여러 번 호출하더라도 항상 같은 객체를 리턴한다는 것.
-그러므로 스프링이 런타임에 생성한 설정 클래스의 메소드가 매번 새로운 객체를 생성 X
- 대신 한 번 생성한 객체를 보관했다가 이후에 동일 객체를 리턴한다.

[두 개 이상의 설정 (클래스) 파일 사용]

-스프링 개발 시 수백 개 이상의 빈을 설정하게 되는데,
관리하기 쉽게 영역별로 설정 파일을 나누는 일 

-
스프링은 한 개 이상의 설정 파일을 이용해서 컨테이너를 생성할 수 있다.

     ① @Autowired 애노테이션

- 스프링의 자동 주입 기능을 위한 것
- 스프링 빈에 의존하는 다른 빈을 자동 주입하고 싶을 때 사용
- 다른 설정 파일에 정의한 빈을 필드에 할당시킴.
-> 필드에 할당받은 것을 이용해서 내부에 필요한 빈을 주입하면 된다.
- 스프링 컨테이너는 @Configuration 설정 클래스 내부를 전체적으로 스프링빈으로 등록하기 때문에,
 
필드에 붙인 @Autowired 대상도 자동적으로 빈으로 등록함
@Autowired
private MemberDao memberDao; //외부 다른 설정 파일에 정의된 빈을 필드에 할당 
@Autowired
private MemberPrinter memberPrinter;
	...

@Bean 
public MemberListPrinter listPrinter() {
	return new MemberListPrinter(memberDao, memberPrinter); 
						  //필드에 할당받은 빈을 주입

    ② @import 애노테이션

- 함게 사용할 설정 클래스를 지정함
@import(AppConf2.class)

- 배열을 이용해서 두 개 이상의 설정 클래스도 지정 O
@import( { AppConf1.class, AppConf2.class } )

[getBean() 메소드의 사용]

- 컨테이너.getBean( 빈 이름, 빈 타입);

- 존재하지 않는 빈 이름 사용 시 오류

- 지정 타입 옳지 않으면 오류
- 해당 타입의 빈 객체가 한 개만 존재할 경우, 빈 이름 지정 없이 빈 타입만으로도 빈 구할 수 있다. :  getBean(빈 타입)

[주입 대상 객체가 모두 빈 객체일 필요는 없다]

-스프링 설정 클래스에서 주입할 객체에는 빈 등록 없이 일반 객체로 생성하여 주입도 O
   
   * 차이는 ?

: 스프링 컨테이너가 객체를 관리하는지 여부
- 빈으로 등록하지 않은 일반 객체의 경우, 스프링 컨테이너로 getBean() 해도 구할 수 X
- 스프링 컨테이너는 객체 관리를 오직 빈으로 등록된 객체를 대상으로만 기능 적용 O
                        (자동 주입, 관리, 빈 객체 생성 등)

728x90