Web/Spring

[Spring] 스프링 컨테이너

깨구르르 2024. 4. 10. 19:10
728x90

컨테이너가 뭔지 궁금하다면 하단 링크 클릭!

2024.03.29 - [Web] - [Web] 웹 서비스 구조

 

1. 스프링 컨테이너 Spring Container란?

 

자바 객체(빈, Bean)의 생명 주기(생성, 관리, 제거 등)를 관리하며,

생성된 자바 객체들에게 추가적인 기능을 제공한다.

스프링 컨테이너는 XML, 어노테이션 기반의 자바 설정 클래스로 만들 수 있다.

스프링 부트 Spring Boot를 사용하기 이전에는 xml을 통해 직접적으로 설정해 주어야 했지만, 

스프링 부트가 등장하면서 대부분 사용하지 않게 되었다.

 

@Configuration 어노테이션이 붙은 클래스를 설정 정보로 사용한다.

클래스 내부에 @Bean 어노테이션이 적힌 메서드를 모두 호출하여 얻은 객체를 스프링 컨테이너에 등록하게 되고,

등록된 객체를 스프링 빈이라고 한다.

 


 

2. 스프링 컨테이너의 기능

 

  • 스프링 컨테이너는 빈의 인스턴스화, 구성, 전체 생명 주기 및 제거까지 관리한다.
    • 컨테이너는 개발자가 정의한 빈을 객체로 만들어 관리하고 개발자가 필요로 할 때 제공
  • 스프링 컨테이너를 통해 원하는 만큼 많은 객체를 가질 수 있다.
  • * 의존성 주입을 통해 어플리케이션의 컴포넌트를 관리할 수 있다.
  • 스프링 컨테이너는 서로 다른 빈을 연결하여 애플리케이션 빈을 연결하는 역할을 한다.
    • 개발자는 모듈 간에 의존 및 결합으로 인해 발생하는 문제로부터 자유로울 수 있음
    • 메서드가 언제 어디서 호출되어야 하는지, 메서드를 호출하기 위해 필요한 매개변수를 준비해서 전달하지 않음(DI)

 

더보기

의존성 주입에 대해 알고 싶으면 아래 클릭!

2024.04.09 - [Web/Spring] - [Spring] 의존성 주입과 제어 역행

 


 

3. 스프링 컨테이너의 종류

 

 

Beanfactory ApplicationContext 두 종류의 인터페이스로 구현되어 있다.

빈 팩토리 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트이고,

빈 팩토리를 좀 더 확장한 것이 애플리케이션 컨텍스트이다.

애플리케이션 컨텍스트는 IoC 방식을 따라 만들어진 일종의 빈 팩토리이다.

주로 사용되는 스프링 컨테이너는 애플리케이션 컨텍스트이다.

 

- BeanFactory

  • 스프링 컨테이너의 최상위 인터페이스이다.
  • 빈을 등록, 생성, 조회 등의 빈을 관리하는 역할을 한다.
  • getBean() 메서드를 통해 빈을 인스턴스화할 수 있다.

 

@Configuration
public class AppConfig {
	
    @Bean
    public OrderService orderService() {
		return new OrderServiceImpl(discountPolicy());
    }
    
    @Bean
    public FixDiscountPolicy discountPolicy() {
    	return new FixDiscountPolicy();
    }
}

 

 

위 사진처럼 스프링 컨테이너 안에 스프링 빈 저장소가 있고, 그 안에 빈이 들어있다.

 

public class Main {
	public static void main(String[] args) {
    	final BeanFactory beanFactory = AnnotationConfigApplicationContext(AppConfig.class);
        final OrderService orderService = beanFactory.getBean("orderService", OrderService.class);
        final Order order = orderService.createOrder(15, "샤프", 3000);
        System.out.println(order.getDiscountPrice());
    }
}

 

위와 같이 BeanFactory를 AnnotationConfigApplicationContext로 정의하되, AppConfig를 구성 정보로 지정한다.

기존에는 개발자가 직접 AppConfig를 사용해서 필요한 객체를 직접 조회했지만,

이제부터는 스프링 컨테이너를 통해서 필요한 스프링 빈 객체를 찾을 수 있다.

getBean 메소드 사용시 만약 OrderService.class를 적어주지 않으면 orderService의 타입은 OrderService가 아니라 Object가 된다.

 

.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4e718207
13:52:20.460 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:52:20.738 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
13:52:20.741 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:52:20.743 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:52:20.745 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:52:20.758 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
13:52:20.767 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
13:52:20.793 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'
1000

 

실행하면 위와 같이 로그가 뜬다.

org.springframework.context.로 시작하는 빈 이름들은 미리 정의된 초기 빈들이고,

appConfig, orderSerivce, discountPolicy가 우리가 등록한 빈이라는 사실을 알 수 있다.

또한, OrderService는 DiscountPolicy를 주입해 주어야 하는데 스프링 컨테이너가 알아서 주입을 해준다.

 


 

- ApplicationContext

애플리케이션 컨텍스트는 BeanFactory의 기능을 상속받아 제공한다.

👉 빈을 관리하고 검색하는 기능을 BeanFactory가 제공하고, 그 외의 부가 기능을 제공함

 

부가 기능

  • MessageSource: 메시지 다국화를 위한 인터페이스
  • EnvironmentCapable: 개발, 운영, 환경변수 등으로 나누어 처리하고, 애플리케이션 구동 시 필요한 정보들을 관리하기 위한 인터페이스
  • ApplicatinoEventPublisher: 이벤트 관련 기능을 제공하는 인터페이스
  • ResourceLoader: 파일, 클래스 패스, 외부 등 리소스를 편리하게 조회

 

public class Main {

    public static void main(String[] args) {
        final ApplicationContext beanFactory = new AnnotationConfigApplicationContext(AppConfig.class);
        final OrderService orderService = beanFactory.getBean("orderService", OrderService.class);
        final Order order = orderService.createOrder(15, "샤프", 3000);
        System.out.println(order.getDiscountPrice());
    }
}

 

아까 작성한 Main 코드에서 BeanFactory를 ApplicationContext로만 바꾸고 실행해보았을 때, 아래와 같은 결과가 뜬다.

 

13:56:08.105 [main] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4e718207
13:56:08.136 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:56:08.391 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
13:56:08.396 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:56:08.398 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:56:08.401 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:56:08.417 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'appConfig'
13:56:08.431 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'orderService'
13:56:08.458 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'discountPolicy'
1000

동일한 결과를 얻어온다는 사실을 알 수 있다.

👉 ApplicationContext도 BeanFactory처럼 빈을 관리할 수 있다는 사실을 알 수 있음

 


 

- BeanFactory vs ApplicationContext

 

둘 다 빈을 관리한다는 공통점이 있는데, 차이점이 무엇이 있을까?

구분 BeanFactory ApplicationContext
공통점 빈을 관리함
차이점 빈 생성
시점
처음으로 getBean() 메소드가 호출된 시점에서야 해당 빈을 생성 Context 초기화 시점에 모든 싱글톤 빈을 미리 로드한 후 애플리케이션 가동 후에는 빈을 지연 없이 받을 수 있음
부가
기능
X - 국제화가 지원되는 텍스트 메시지 관리
- 이미지 같은 파일 자원을 로드
- 리스너로 등록된 빈에게 이벤트 발생 알림

👉 부가 기능과 빈을 지연 없이 얻을 수 있다는 장점 때문에 ApplicationContext를 실제 개발에서 주로 사용함

 


 

4. 스프링 컨테이너를 사용하는 이유

 

1) 느슨한 결합, 높은 캡슐화

객체를 생성하기 위해서는 new 생성자를 사용해야 한다.

그로 인해 애플리케이션에서는 수많은 객체가 존재하고 서로를 참조하게 된다.

그런데, 객체 간의 참조가 많으면 의존성이 높아지게 된다.

이는 낮은 결합도와 높은 캡슐화를 지향하는 객체지향 프로그래밍의 핵심과는 먼 방식이다.

따라서 객체 간의 의존성을 낮추어(느슨한 결합) 결합도는 낮추고, 높은 캡슐화를 위해 스프링 컨테이너가 사용된다.

 

2) 클래스 의존성 제거

또한, 기존의 방식으로는 새로운 기능이 생기게 되면 변경 사항들을 수작업으로 수정해야 한다.

프로젝트가 커질수록 의존도는 높아질 것이고, 그에 따라 코드의 변경도 많아질 것이다.

하지만, 스프링 컨테이너를 사용하면 구현 클래스에 있는 의존성을 제거하고 인터페이스에만 의존하도록 설계할 수 있음

 


 

5. 스프링 컨테이너 생성 과정

 

1) 비어있는 스프링 컨테이너 생성(스프링 컨텍스트 초기화)

2) 스프링 설정 파일들을 기반으로 하여 빈을 등록

  • Configuration Metadata를 사용하며, 파라미터로 넘어온 설정 클래스 정보를 사용해 스프링 빈 등록
  • 스프링 빈 조회에서 상속 관계가 있을 경우 부모 타입으로 조회하면, 자식 타입도 함께 조회됨
    👉모든 자바 객체의 최고 부모인 object 타입으로 조회하면 모든 스프링 빈 조회가 가능함

3) 스프링 설정 파일들을 기반으로 하여 스프링 의존관계를 주입

 


 

6. 싱글톤 패턴 Singleton

 

- 정의

 

소프트웨어 디자인 패턴에서 싱글톤 패턴을 따르는 클래스는,

생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고

최초 생성 이후에 호출된 생성자최초의 생성한 객체를 리턴한다.

 


 

- 사용 이유

 

싱글톤 패턴을 사용하지 않아 요청을 할 때마다 새로운 객체를 생성한다고 가정했을 때,

요청이 상당히 많은 트래픽 사이트에서는 계속 객체를 생성하게 되면 메모리 낭비가 심하기 때문이다.

 


 

- 구현 방법

 

  • static 객체를 통해서 해당 객체를 1개만 생성할 수 있도록 지정함
  • static 메소드를 통해서 외부에서 호출할 수 있도록 제한함
  • new 연산자를 통해서 객체를 만드는 것private 생성자를 통해서 제한

 

개발자가 싱글톤 패턴을 구현하는 방법은 여러 가지가 있는데,

이 방법은 객체를 미리 생성해두는 가장 단순하고 안전한 방법이다.

싱글톤 패턴은 이미 만들어진 객체를 공유해서 효율적으로 사용할 수 있다.

 

싱글톤 패턴을 사용하게 되면 유연성이 떨어지게 된다는 문제점 또한 존재하는데,

🌟스프링 컨테이너는 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리한다. 🌟

이러한 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하고 객체를 싱글톤으로 유지할 수 있다.

 


 

- 주의점

 

객체 인스턴스를 하나만 생성해서 공유하는 상황에서 객체 인스턴스를 공유하기 때문에 객체 상태를 유지하게 설계하면 안 된다불변 객체를 만드는 방법은 아래와 같다.

 

  • 모든 필드를 private으로 선언하기
  • 모든 필드에 대해 setter 메소드를 제공하지 않기
    :  setter 메소드를 제공하면 외부에서 객체의 상태를 변경할 수 있음
    👉불변 객체를 만들기 위해서는 setter 메소드를 제공하지 않거나 최소화해야 함
  • 생성자를 통해 필드 초기화하기
    : 생성자를 통해 필드를 초기화하게. 생성자 내에서 필드의 값이 변경되지 않도록 보장
  • mutable 객체(ArrayList, HashMap, StringBuilder 등)를 참조하는 경우에는 해당 객체를 복사하여 참조
  • 클래스를 final로 선언하기
    : 상속을 막아 하위 클래스의 생성 방지

 


 

참고

 

 

728x90