본문 바로가기

Tech/Spring

스프링 싱글톤 컨테이너의 의존관계 주입과 빈 생명주기

 🚩목차

    스프링 컨테이너와 스프링 빈

    스프링 컨테이너란

     스프링 컨테이너란 스프링 빈(객체)을 싱글톤 패턴으로 생성하고 관리하는 저장 공간입니다. 객체를 싱글톤 패턴으로 관리한다는 말은 스프링 애플리케이션 내에서 해당 클래스 인스턴스를 단 하나만 생성해서 여러 컴포넌트에서 재활용 한다는 의미입니다.
     
     스프링 컨테이너는 BeanFactory 인터페이스를 의미합니다. getBean()으로 저장소에 등록된 빈을 가져오거나, getType()으로 빈 타입을 확인하는 등 다양한 메소드를 제공합니다. 현대에는 BeanFactory를 구현해 다양한 기능을 추가 탑재한 ApplicationContext 인터페이스를 스프링 컨테이너로 주로 사용합니다.
     

    빈 수동 등록

     ApplicationContext는 저장소에 등록할 스프링 빈의 정보를 어디서 가져올까요? 바로 개발자가 전달한 빈(객체) 설정 정보입니다. 마치 레고의 설계도 처럼 개발자가 빈 설정 정보를 전달하면, 스프링 컨테이너는 해당 설정 정보를 토대로 객체를 싱글톤 패턴으로 단 하나만 생성해 보관합니다.
     
     그럼 개발자는 어떻게 설정 정보를 넘길 수 있을까요? 바로 생성자 매개변수로 전달하면 됩니다. ApplicationContext는 빈 설정 정보가 어떤 형식으로 작성되었느냐에 따라 그 구현체가 달라지는데요, 자바 클래스로 작성하면 AnnotationConfigApplicationContext 으로, XML 형식으로 작성하면 XmlApplicationContext 으로 구현체를 선택하면 됩니다.
     
     스프링 컨테이너가 다양한 설정 정보를 토대로 빈을 생성할 수 있는 이유는 바로 BeanDefinition 추상화에 의존하기 때문입니다. 다음은 스프링 공식 문서에 작성된 Bean Definition에 관한 설명입니다.

    A bean definition is essentially a recipe for creating one or more objects. The container looks at the recipe for a named bean when asked and uses the configuration metadata encapsulated by that bean definition to create (or acquire) an actual object.

     
     Bean Definition은 객체를 생성할 때 참고하는 레시피라고 합니다. 그리고 빈 데피니션 안에 담긴 설정 메타데이터를 실제 객체를 만들어낼 때 활용한다고 작성되어 있습니다.
     

    빈 자동 등록

     위처럼 스프링 빈은 개발자가 작성한 설정정보를 바탕으로 생성될 수도 있지만, 모든 객체에 대한 레시피를 일일이 작성한다면 매우 번거로워질 것입니다. 따라서 스프링은 프로젝트에서 객체를 자동으로 스캔하고 컨테이너에 등록할 수 있도록 컴포넌트 스캔을 제공합니다.
     
     방법은 매우 간단합니다. 스프링 빈으로 등록할 클래스에 클래스 수준 애너테이션 @Component를 붙여주기만 하면 됩니다. 스프링은 애플리케이션 시작 시점에 자동으로 @Component가 붙은 클래스를 스캔해 싱글톤 빈으로 등록합니다.
     
     자동 스캔 대상이 되는 패키지 범위는 기본적으로 @ComponentScan 애너테이션이 붙은 설정 클래스의 패키지 위치부터 입니다. 스캔 범위를 바꾸려면 @ComponentScan 애너테이션의 옵션인 basePackage로 변경하면 됩니다.

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @SpringBootConfiguration
    @EnableAutoConfiguration
    @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
    		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
    public @interface SpringBootApplication {
    	...
    }

     
     Springboot의 경우 @SpringBootApplication 애너테이션이 @ComponentScan을 포함하고 있어 프로젝트 최상단 패키지 부터 컴포넌트 스캔을 수행합니다. 또한 @Component 외에도 @Controller, @Service, @Repository, @Configuration이 붙은 클래스는 스캔 대상이 됩니다.
     

    의존관계 주입

    4가지 방법

     스프링 컨테이너는 특정 객체가 의존하고 있는 객체에 @Autowired 애너테이션을 붙이면, 컨테이너에서 해당 빈을 조회해 주입합니다. 객체에 의존 관계를 자동으로 주입하는 방법은 4가지입니다. Spring은 공식적으로 4가지 방법 중 생성자 주입을 권장하고 있습니다.

    1. 생성자 주입
      • 생성자 레벨에 @Autowired 애너테이션을 붙이고 매개변수에 의존할 객체를 명시합니다.
      • 만약 해당 클래스에 생성자가 단 하나만 존재한다면 @Autowired 애너테이션을 생략해도 동일하게 동작합니다.
    2. 필드 주입
      • 의존할 객체 필드에 @Autowired 애너테이션을 붙입니다.
    3. 수정자 주입
      • 의존할 객체 필드를 수정하는 수정자(Setter)에 @Autowired 애너테이션을 붙입니다.
    4. 일반 메서드 주입

     

    의존관계 자동 주입 시 발생 가능한 문제와 해결책

    1. 주입할 빈이 스프링 컨테이너에 존재하지 않는 경우
      • @Autowired(requried=false) required 옵션에 false 값을 작성합니다. 컨테이너에 주입할 빈이 존재하지 않는 경우 , 수정자 메서드 자체가 호출되지 않습니다.
      • 주입될 객체에 @Nullable 애너테이션을 작성합니다. 컨테이너에 주입할 빈이 존재하지 않는 경우, null이 입력됩니다.
      • 주입될 객체에 Optional로 감쌉니다. 컨테이너에 주입 대상이 존재하지 않는 경우, Optional.empty가 입력됩니다.
    2. 주입할 빈이 스프링 컨테이너에 여러개 존재하는 경우
      • @Autowired 가 붙은 필드의 이름을 빈 이름과 동일하게 매칭시킵니다.
      • @Qualifier 애너테이션을 붙여 식별자를 추가합니다.
      • @Primary를 클래스 레벨에 붙여 해당 빈에 다른 빈들 보다 주입 우선권을 줍니다.
      • 만약 동일 타입으로 조회한 빈이 모두 필요할 경우, Map 또는 List로 받아 사용합니다.

     

    빈 생명주기와 빈 스코프

    스프링 빈 생명주기

     생명주기란 객체의 생성부터 소멸까지의 과정을 표현한 것입니다. 스프링 빈은 애플리케이션 실행 도중 다음의 생명주기를 갖습니다.

    스프링 컨테이너 생성 > 스프링 빈 생성 > 의존관계 주입 > 객체 초기화 콜백 메서드 호출 > 사용됨 > 종료 콜백 메서드 호출 > 소멸

     

    빈 스코프

     빈 스코프란 스프링 컨테이너가 특정 객체의 생명주기 중 어느 범위까지 관여하는지를 표현한 것입니다.

    • 싱글톤: 스프링 컨테이너는 싱글톤 스코프 객체의 생성부터 소멸까지 전 범위를 관여합니다.
    • 프로토타입: 스프링 컨테이너는 프로토타입 스코프 객체의 생성부터 초기화 콜백까지 범위를 관여합니다.

     

    정리

    1. 스프링 컨테이너는 다양한 설정 정보를 토대로 객체를 생성해 싱글톤으로 관리한다. 컴포넌트 스캔으로 빈을 자동 등록 할 수도 있다.
    2. 의존관계 자동 주입은 기본적으로 생성자를 사용하되, 변경 가능성이 있는 의존관계에는 수정자를 사용한다.
    3. 스프링 빈은 생명주기를 가지며, 빈 스코프는 스프링 컨테이너가 빈 생명주기의 어느 범위까지 관여하느냐를 나타낸 것이다.

    Reference