본문 바로가기
Spring

자바 스프링 - DI(Dependency Injection)의존성 주입 정리

by onggury 2023. 3. 28.

의존이란?

- 객체와 객체 간의 의존을 뜻합니다. 하나의 객체는 다른 여러 객체들과 의존 관계를 지닙니다.

 

달리 말하면, 여러 객체들은 어떤 하나의 객체에 의존할 수 있다는 겁니다.

물론 의존 대상이 하나가 아니라 여러개가 될 수 있기도 합니다.

여기서 포인트는 어떤 객체는 필요로하는(의존하는) 또 다른 객체가 있다는 겁니다.

 

 

 

간단히 예를 들어, 사용자 정보를 저장하고 받아오는 Member라는 DO(Data Object)클래스가 있다고 가정합니다.

public class Member {
	private String email;
	private String password;
	private String name;
  		.
    		.
    		.
            
	public void setEmail(Long email) {
		this.email = email;
	}

	public String getEmail() {
		return email;
	}
    		.
            	.
                .
}

해당 클래스에서는 Getter메서드와 Setter메서드만 존재하는, 데이터만 관리하는 클래스입니다.

그리고 이 DO클래스를 필요로하는 DAO(Data Access object) 객체는 DO클래스에서 Member의 정보를 받아와서 값을 처리하는 클래스입니다.

DAO 객체는 DO 객체에 의존하고 있다고 볼 수 있습니다.

 

 


 

 

의존 주입 방식

의존 주입 방식으로는 두 가지 방법이 있습니다.

 

    1. 생성자 주입 방식

말 그대로 생성자를 생성함과 동시에 객체를 주입하는 방법입니다.

 

또한 Bean 객체를 생성하는 시점에 모든 의존 객체를 주입합니다.

Bean에 대해서는 아래에서 확인해 보겠습니다. 일단 생성되는 시점에 모든 의존 객체를 주입한다는 것만 아셔도 됩니다.

 

 

    2. 객체 주입 방식

setter 메서드로 객체를 주입하는 방식입니다.

setter 메서드 이름을 통해 어떤 의존 객체가 주입되는지 알 수 있습니다.

그리고 이 땐 NullPointerException 예외가 발생할 수 있습니다.

 

public class MemberRegisterService {  // 생성자 주입 방식

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

public class ChangePasswordService {  // 객체 주입 방식
	
   	private MemberDao memberDao;
    	public ChangePasswordService() {
   	}
    
    public setMemberDao(MemberDao memberDao){
    	this.memberDao = memberDao;
    }
    ...
}

////////////////////////////////////////////////////////////

public class Main {

	public static void main(String [] args) {
    	MemberDao memberDao = new MemberDao(); // memberDao 객체 생성
        MemberRegisterService regSvc = new MemberRegisterService(memberDao); // 생성자 주입 방식
        ChangePasswordService pwdSvc = new ChangePasswordService();
        pwdSvc.setMemberDao(memberDao);  // 객체 주입 방식
        
    }
}

 


 

 

자바 스프링에서의 의존

스프링에서는, AnnotationConfigApplicationContext를 통해서 스프링 컨테이너를 만듭니다.

 

스프링 컨테이너를 만들면 이에 대한 설정 클래스를 통해 등록되어진 클래스를 찾아 가져옵니다.

설정 클래스는 @Configuration 어노테이션을 사용합니다.

 

그리고 클래스를 등록하기 위해서 @Bean 어노테이션을 활용합니다.

메서드에 @Bean 어노테이션을 붙이면 스프링 컨테이너에 이 Bean객체를 등록하게 됩니다.

 

예시를 보도록 하겠습니다.

 

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

@Configuration 
public class AppContext {
	
	@Bean
	public Greeter greeter() {
		Greeter g = new Greeter();
		g.setFormat("%s, 안녕하세요");
		
		return g;
	}
}

@configuration 어노테이션을 붙임으로써 설정 클래스임을 알려줍니다.

그리고 gretter() 메서드에 @Bean 어노테이션을 붙임으로써 스프링 컨테이너에 Bean 객체를 등록합니다.

또한 setFormat()메서드를 통해 해당 문자열을 format이란 변수에 초기화 시킵니다.

 

public class Greeter {
	
	private String format;
	
	public Greeter() {
		// 빈 생성자를 만들어주는것이 좋다.
	}
	
	public String greet(String guest) {
		return String.format(this.format, guest);
	}
	
	public void setFormat(String format) {
		this.format = format;
	}
	
}

위 클래스는 greet라는 메서드를 통해 문자열을 받아 지정된 format변수와 입력받은 문자열을 병합하여 반환합니다.

 

 

public class Main {

	public static void main(String[] args) {

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppContext.class);
		// 스프링 컨테이너에 대한 설정 클래스인 AppContext
		// AnnotationConfigApplicationContext를 통해서 스프링 컨테이너를 만드는 것이다.
		// 스프링 컨테이너 안에는 설정 클래스에 있는 greeter라는 클래스가 자동으로 등록된다.
		
		
		Greeter g = context.getBean("greeter", Greeter.class);
				
		String msg = g.greet("스프링");
		System.out.println(msg);
}

메인 클래스를 보면 AnnotationConfigApplicationContext 객체로 설정 클래스인 AppContext 객체를 사용합니다.

그리고 .getBean 메서드를 통해 설정 클래스에 있는 Bean으로 등록된 Greeter객체를 g라는 레퍼런스 변수로 생성합니다.

동시에 Greeter객체를 생성 및 초기화 하면서 .setFormat 메서드로 문자열이 초기화 되고 .greet라는 메서드를 통해 문자열 값을 넘겨줌으로써 this.format="%s, 안녕하세요", guest="스프링" 이 되면서 "스프링, 안녕하세요"라는 결과가 나옵니다.

 

이는 스프링에서의 의존성 주입 덕분에 가능하게 된 결과입니다.

 

 

그리고 스프링 컨테이너가 생성한 Bean 객체는 싱글톤 객체 입니다.

이를 확인하기 위해 아래 코드를 실행해보겠습니다.

 

	public static void main(String[] args) {

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppContext.class);
		
		Greeter g1 = context.getBean("greeter", Greeter.class);
		Greeter g2 = context.getBean("greeter", Greeter.class);
		
		System.out.println("g1 == g2 : " + (g1 == g2));  // 싱글톤
		
	}

분명 Greeter 라는 타입의 래퍼런스 변수로 g1, g2를 각각 만들었는데 .getBean으로 스프링 컨테이너에 등록된 Bean클래스를 가져오게 되면서 g1과 g2 래퍼런스 변수는 같은 주소값을 가집니다.

 

다음 글에서는 의존 자동 주입에 대해 정리해보겠습니다.