본문 바로가기

Spring/Core

스프링 컨테이너

스프링 컨테이너

스프링 컨테이너는 자바 객체의 생명 주기(객체의 생성, 소멸)를 관리하여 등록된 빈(Bean)을 조회하고 사용할 수 있으며, 부가기능(환경 변수 설정, 어플리케이션 이벤트, 편리한 리소스 조회 등)을 제공한다.

 

스프링 컨테이너는 IoC, DI랑도 연관이 깊다. 기존 개발자는 new 키워드를 사용해 직접 객체를 생성하기도 하고 코드 수정이 필요할 경우 새로운 객체로 변경하는 등 직접 관리를 해왔다. 여기에서는 OCP, DIP 위반이 일어났고 스프링 프레임워크는 그러한 문제를 해결하기 위해 스프링 컨테이너를 사용하여 개발자를 대신해서 객체를 직접 관리하게 되었다. 스프링 컨테이너는 @Autowired가 적용된 코드에 의존관계 주입을 한다.

 

스프링 컨테이너는 빈을 싱글톤으로 관리하며 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리한다. 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라고 부르며, 스프링 컨테이너는 싱글톤 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있다. (싱글톤 패턴을 위한 지저분한 코드가 들어가지 않아도 되고 DIP, OCP, 테스트, private 생성자로 부터 자유롭게 싱글톤을 사용할 수 있다.)

 

 

BeanFactory

스프링 컨테이너의 최상위 인터페이스로 기능은 단순하게 스프링 빈을 관리하고 조회하는 역할을 담당한다.

 

 

ApplicationContext

BeanFactory 기능을 모두 상속받아서 제공한다. 또한, 어플리케이션을 개발하면서 필요한 부가기능을 제공한다.

 

ApplicatonContext가 제공하는 부가기능

메시지소스를 활용한 국제화 기능

 - 예를 들어서 한국에서 들어오면 한국어로, 영어권에서 들어오면 영어로 출력 환경변수

로컬, 개발, 운영등을 구분해서 처리 애플리케이션 이벤트

 - 이벤트를 발행하고 구독하는 모델을 편리하게 지원

편리한 리소스 조회

 - 파일, 클래스패스, 외부 등에서 리소스를 편리하게 조회

 

AppConfig.java

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

@Configuration 적용 후 빈으로 생성할 메서드에 @Bean 을 적용시켜주면 스프링 컨테이너에 등록이 된다.

그렇게 등록된 빈은 메서드 이름을 사용하게 되고 이름을 명시적으로 부여할 수도 있다. 주의해야할 점은 빈 이름은 항상 다른 이름을 부여해야 한다. 같은 이름을 부여하면 다른 빈이 무시되거나, 기존 빈을 덮어버리거나 설정에 따라 오류가 발생한다.

Bean Definition

스프링은 BeanDefinition 을 통해서 각각의 빈 정보를 생성하며 스프링 컨테이너는 이 정보를 통해 빈을 생성한다.

BeanDefinition은 추상메서드로 자바 버전과 xml 버전의 설정 정보를 통해서 빈 정보를 생성할 수 있으며 최근에는 xml 설정을 사용하지 않는 추세이다. 자바 버전을 사용할 경우 AnnotationConfigApplicationContext 를 사용하게 되며 AnnotatedBeanDefinitionReader 를 사용해서 AppConfig.class 를 읽고 BeanDefinition 을 생성한다.

 

BeanDefinitionTest.java

public class BeanDefinitionTest {

    AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);

    @Test
    @DisplayName("빈 설정 메타정보 확인")
    void findApplicationBean() {
        String[] beanDefinitionNames = ac.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            BeanDefinition beanDefinition = ac.getBeanDefinition(beanDefinitionName);

            if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {
                System.out.println("beanDefinitionName : " + beanDefinitionName +
                " beanDefinition : " + beanDefinition);
            }
        }
    }
}

BeanDefinition 정보

  • BeanClassName: 생성할 빈의 클래스 명(자바 설정 처럼 팩토리 역할의 빈을 사용하면 없음)
  • actoryBeanName: 팩토리 역할의 빈을 사용할 경우 이름, 예) appConfig
  • factoryMethodName: 빈을 생성할 팩토리 메서드 지정, 예) memberService
  • Scope: 싱글톤(기본값)
  • azyInit: 스프링 컨테이너를 생성할 때 빈을 생성하는 것이 아니라, 실제 빈을 사용할 때 까지 최대한 생성을 지연처리 하는지 여부
  • InitMethodName: 빈을 생성하고, 의존관계를 적용한 뒤에 호출되는 초기화 메서드 명
  • DestroyMethodName: 빈의 생명주기가 끝나서 제거하기 직전에 호출되는 메서드 명
  • Constructor arguments, Properties: 의존관계 주입에서 사용한다. (자바 설정 처럼 팩토리 역할의 빈을 사용하면 없음)

@Configuration의 비밀

AppConfig에 적용된 @Configuration은 CGLIB 라이브러리를 이용해 바이트 코드를 아래와 같이 조작한다.

 

AppConfig.Java

@Configuration
public class AppConfig {
    @Bean
    public MemberService memberService() {
    	if(memberService == null) {
        	return new MemberServiceImpl(memberRepository());
        } else {
        	return memberService;
        }
    }

    @Bean
    public MemberRepository memberRepository() {
    	if(memberRepository == null) {
        	return new MemoryMemberRepository();
        } else {
        	return memberRepository;
        }
    }
}

위처럼 @Bean이 붙은 메서드마다 이미 스프링 빈이 존재하면 존재하는 빈을 반환하고, 스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어진다. 덕분에 싱글톤이 보장되는 것이다.

 

 


참고 자료

https://inf.run/ifWX