본문 바로가기
WEB/SPRING

[Spring] @Configuration 설정 클래스의 @Bean 설정과 싱글톤

by snow_white 2022. 6. 3.

스프링 Bean 설정과 싱글톤

먼저, 이전 포스트에서 작성한 AppCtx 클래스의 일부 코드를 살펴보자.

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;
	}
}

memberRegSvc() 메서드와 changePwdSvc() 메서드는 둘 다 memberDao() 메서드를 실행하고 있다.

그리고 memberDao() 메서드는 매번 새로운 MemberDao 객체를 생성해서 리턴한다.

❓ new 키워드로 새로운 MemberDao 객체를 생성해서 리턴하면 두 객체는 서로 다른 객체 아닌가 라는 생각을 할 수 있다.

 

스프링 컨테이너가 생성한 싱글톤 객체이기 때문에 걱정하지 않아도 된다.

스프링 컨테이너는 @Bean이 붙은 메서드에 대해 한 개의 객체만 생성한다.

이는 다른 설정 메서드에서 memberDao()를 몇 번을 호출하더라도 항상 같은 객체를 리턴한다는 것이다.

 

❓ 그래도 이해가 되지 않는다면?

스프링은 설정 클래스를 그대로 사용하지 않는다.

설정 클래스를 상속한 새로운 설정 클래스를 만들어서 사용한다. 아래의 예시 코드를 살펴보자.

public class AppCtxExt extends AppCtx{

	private Map<String, Object> beans = ...;
    
    @Override
    public MemberDao memberDao(){
    	if(!beans.containsKey("memberDao"))
        	beans.put("memberDao", super.memberDao());
            
        return (MemberDao) beans.get("memberDao");
    }
}

스프링이 런타임에 생성한 설정 클래스의 memberDao() 메서드는 매번 새로운 객체를 생성하지 않는다.

대신 한 번 생성한 객체를 보관했다가 이후에는 동일한 객체를 리턴한다.

따라서 memberRegSvc() 메서드와 changePwdSvc() 메서드에서 memberDao() 메서드를 각각 실행해도 동일한 memberDao 객체를 사용하게 되는 것이다.

 

 

두 개 이상의 설정 파일 사용하기

스프링을 이용해서 어플리케이션을 개발하다보면 수많은 빈을 설정하게 된다.

설정하는 빈의 개수가 증가하면 한 개의 클래스 파일에 설정하는 것보다 영역별로 설정 파일을 나누면 관리하기 편해진다.

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

 

아래 두 파일은 이전 포스트에서 작성한 AppCtx.java의 빈 설정을 나눠서 설정한 것이다.

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

import spring.MemberDao;
import spring.MemberPrinter;

@Configuration
public class AppConf1 {

	@Bean
	public MemberDao memberDao() {
		return new MemberDao();
	}
	
	@Bean
	public MemberPrinter memberPrinter() {
		return new MemberPrinter();
	}
}

아래의 @Autowired 애노테이션은 스프링의 자동 주입 기능을 위한 것이다.

해당 타입의 빈을 찾아서 필드에 할당해주게 된다.

아래 설정의 경우 스프링 컨테이너는 MemberDao 타입의 빈을 memberDao 필드에 할당한다.

AppConf1 클래스에 MemberDao 타입의 빈을 설정했으므로 AppConf2 클래스의 memberDao 필드에는 AppConf1 클래스에서 설정한 빈이 할당된다.

 

@Aurowired 애노테이션을 이용해서 다른 설정 파일에 정의한 빈을 필드에 할당했다면 설정 메서드에서 이 필드를 사용해서 필요한 빈을 주입하면 된다.

import org.springframework.beans.factory.annotation.Autowired;
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 AppConf2 {
	@Autowired
	private MemberDao memberDao;
	@Autowired
	private MemberPrinter memberPrinter;
	
	@Bean
	public MemberRegisterService memberRegSvc() {
    	// 필드로 주입받은 빈 객체를 생성자를 이용해서 주입
		return new MemberRegisterService(memberDao);
	}
	
	@Bean
	public ChangePasswordService changePwdSvc() {
		ChangePasswordService pwdSvc = new ChangePasswordService();
		pwdSvc.setMemberDao(memberDao);
		return pwdSvc;
	}
	
	@Bean
	public MemberListPrinter listPrinter() {
		return new MemberListPrinter(memberDao, memberPrinter);
	}
	
	@Bean
	public MemberInfoPrinter infoPrinter() {
		MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
		infoPrinter.setMemberDao(memberDao);
		infoPrinter.setPrinter(memberPrinter);
		return infoPrinter;
	}
	
	@Bean
	public VersionPrinter versionPrinter() {
		VersionPrinter versionPrinter = new VersionPrinter();
		versionPrinter.setMajorVersion(5);
		versionPrinter.setMinorVersion(0);
		return versionPrinter;
	}
}

 

만약 설정 클래스가 두 개 이상이어도 스프링 컨테이너를 생성하는 코드는 크게 다르지 않다.

다음과 같이 파라미터로 설정 클래스를 추가로 전달하면 된다.

ctx = new AnnotationConfigApplicationContext(AppConf1.class, AppConf2.class);

'WEB > SPRING' 카테고리의 다른 글

[Spring] 빈(Bean) 🟢  (0) 2022.06.03
[Spring] 컴포넌트 스캔  (0) 2022.06.03
[Spring] Inversion of Control  (0) 2022.06.03
[Spring] Dependency Injection  (0) 2022.06.02
[Spring] STS설정 및 스프링 한글깨짐 방지 설정  (0) 2022.06.02

댓글