본문 바로가기
to Developer/Spring

Spring Container, Bean

by EH헌 2024. 1. 30.

Spring Container (스프링 컨테이너)

스프링 컨테이너는 자바 객체의 생명 주기를 관리하며, 생성된 자바 객체들에게 추가적인 기능을 제공하는 역할을 한다.
스프링에서는 자바 객체를 빈(Bean)이라 한다.
스프링 컨테이너의 종류에는 BeanFactoryApplicationContext가 있다.
둘 다 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 한다.
ApplicationContext가 BeanFactory의 빈 관리 기능들을 상속 받았고, 그 외에 국제화 등의 추가적인 기능을 갖고 있어 스프링 컨테이너라고 하면 보통 ApplicationContext라고 한다.


Spring Container 사용하는 이유

먼저, 객체를 생성하기 위해서는 new 생성자를 사용해야 한다. 그로 인해 애플리케이션에서는 수많은 객체가 존재하고 서로를 참조하게 된다.
객체 간의 참조가 많으면 많을수록 의존성이 높아지게 된다.
이는 낮은 결합도와 높은 캡슐화를 지향하는 객체지향 프로그래밍의 핵심과는 먼 방식이다.
따라서, 객체 간의 의존성을 낮추어 결합도는 낮추고, 높은 캡슐화를 위해 스프링 컨테이너가 사용된다.


Bean

  • 컨테이너 안에 들어있는 객체
  • 컨테이너에 담겨있으며, 필요할 때 컨테이너에서 가져와서 사용
  • @Bean을 사용해 등록하거나 xml을 사용해 등록하고, Bean으로 등록된 객체는 쉽게 주입하여 사용 가능

Bean 생명주기

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존 관계 주입 -> 초기화 콜백 -> 사용 -> 소멸전 콜백 -> 스프링 종료

  • 스프링 컨테이너에 의해 생명주기 관리
  • 스프링 컨테이너 초기화 시 빈 객체 생성, 의존 객체 주입 및 초기화
  • 생성과 의존관계 주입과 초기화 분리
    • 의존관계 주입(생성자 주입)은 필수정보를 받고 메모리 할당을 통해 객채 생성 책임
    • 초기화는 생성된 값들을 활용해 외부 커넥션을 연결하는 등 무거운 작업 수행
    • 명확하게 분리하는 것이 유지보수 관점에서 좋다.
  • 싱글톤 빈들은 컨테이너가 종료되기 직전에 소멸전 콜백이 발생

초기화와 소멸 메서드는 애노테이션으로 @PostConstruct, @PreDestroy 를 사용하는 것이 권장된다.

Bean Scope

  • 싱글톤
    • spring 프레임워크의 기본이 되는 스코프
    • 스프링 컨테이너 시작과 종료까지 1개의 객체로 유지
  • 프로토타입
    • 빈의 생성, 의존관계 주입, 초기화까지만 관여하고 이후에는 컨테이너에서 관리하지 않는 스코프
    • 따라서 매번 요청마다 새로 만들어짐
    • 싱글톤은 스프링이 뜰때 생성되는데 반해, 프로토타입은 요청할때 생성됨
  • 웹 스코프
    • request : 각 요청이 들어오고 나갈때까지 유지
    • session : 세션이 생성되고 종료될때까지 유지
    • application : 웹의 서블릿 컨텍스트와 동일한 생명주기를 갖는 스코프
      • 서블릿 컨텍스트는 web application내에 있는 모든 서블릿들을 관리하며 정보공유할 수 있게 도와 주는 역할 을 하는데, 톰캣 컨테이너가 실행 시 애플리케이션 하나당 한개의 서블릿컨텍스트가 생성된다.
      • 생명 주기는 보통 톰캣의 시작과 종료와 일치한다.

싱글톤 vs 스프링 싱글톤

1. 싱글톤

public class Person { 

    private static Person instance; 

    private Person() {
         throw new IllegalStateException("Private Constructor"); 
    } 

    public static Person getInstance() {
         if (instance == null) {
              instance = new Person(); 
              } 
              return instance; 
        }     
    }
}

public class Singleton { 
    private Singleton(){} 

    public static Singleton getInstance() { 
        return LazyHolder.INSTANCE; 
    }

    private static class LazyHolder { 
        private static final Singleton INSTANCE = new Singleton(); 
    } 
}

위 코드는 자바 언어를 사용해 싱글톤 패턴은 구현하는 전형적인 코드이다.
싱글톤 패턴은 전역 상태를 이용할 수 있다는 장점이 있지만 다음과 같은 문제점으로 인해 안티 패턴으로도 불린다.

  • private 생성자를 갖고 있어 상속이 불가능하다.
    • 싱글톤은 자신만이 객체를 생성할 수 있도록 private으로 제한한다. 하지만 상속을 통해 다형성을 적용하기 위해서는 기본 생성자가 필요하므로 private으로 인해 객체지향의 장점을 적용할 수 없다. 또한 싱글톤을 구현하기 위해서는 객체지향적이지 못한 static 필드와 static 메서드를 사용해야 한다.
  • 테스트하기 힘들다.
    • 싱글톤은 생성 방식이 제한적이기 때문에 Mock 객체로 대체하기 어려우며, 동적으로 객체를 주입하기도 힘들다.
  • 서버 환경에서는 싱글톤이 1개만 생성됨을 보장하지 못한다.
    • 서버에서 클래스 로더 를 어떻게 구성하느냐에 따라 싱글톤 클래스임에도 불구하고 1개 이상의 객체가 만들어질 수 있다. 따라서 자바 언어를 이용한 싱글톤 기법은 서버 환경에서 싱글톤이 꼭 보장된다고 볼 수 없다. 또한 여러 개의 JVM에 분산돼서 설치되는 경우 독립적으로 객체가 생성된다.
  • 전역 상태를 만들 수 있기 때문에 바람직하지 못하다.
    • 싱글톤의 정적 메서드를 사용하면 언제든지 해당 객체를 사용할 수 있고, 전역 상태로 사용되기 쉽다. 아무 객체나 자유롭게 접근하고 수정하며 공유되는 전역 상태는 객체지향 프로그래밍에서 권장하지 않는다.

2. 스프링 싱글톤

객체의 생성을 스프링에 위임함으로써 스프링 컨테이너가 관리하여 자바 언어 레벨에서 직접 구현하기 위한 내용들이 모두 제거되어 앞선 싱글톤의 모든 단점들이 제거된다.

  • private 생성자가 필요 없어 상속이 가능해진다.
  • 테스트하기 편하다.
  • 프레임워크를 통해 1개의 객체 생성을 보장받을 수 있다.
  • 객체지향적으로 개발할 수 있다.

Annotaion

Annotation은 프로그램에게 추가적인 정보를 제공하는 메타데이터이다.

  • 자바 코드에 특별한 의미를 부여한 주석으로 컴파일러를 위한 정보를 제공하기 위한 용도
  • 동작 순서
    • 애노테이션 정의
    • 원하는 위치에 배치
    • 코드가 실행되는 중에 Reflection을 이용하여 추가 정보를 획득하여 기능 실시
  • Reflection
    • Reflection을 사용하면 컴파일 타임에 인터페이스, 필드, 메소드의 이름을 알지 못해도 실행 중에 클래스, 인터페이스, 필드 및 메소드에 접근할 수 있다. 또한 새로운 객체의 인스턴스화 및 메소드 호출을 허용한다.
    • Annotation 자체는 아무런 동작을 가지지 않는 단순한 표식일 뿐이지만, Reflection을 이용하면 Annotation의 적용 여부와 엘리먼트 값을 읽고 처리할 수 있다.
    • Spring 컨테이너(BeanFactory)에서 객체가 호출되면 객체의 인스턴스를 생성하게 되는데 이 때 필요하게 된다. 즉, 프레임워크에서 유연성있는 동작을 위해 쓰인다.
    • Reflection을 이용하면 Annotation 지정만으로도 원하는 클래스를 주입할 수 있다.

Spring Annotation

  • @ComponentScan
    • @Component, @Service, @Repository, @Controller, @Configuration이 붙은 클래스 Bean들을 찾아서 Context에 bean을 등록해주는 애노테이션
    • 전부 다 @Component를 사용하지 않고 @Repository 등으로 분리해서 사용하는 이유는, 예를 들어 @Repository는 DAO에서 발생할 수 있는 unchecked exception들을 스프링의 DataAccessException으로 처리할 수 있기 때문이다.
    • 또한 가독성에서도 해당 애노테이션을 갖는 클래스가 무엇을 하는지 단 번에 알 수 있다.
  • @EnableAutoConfiguration
    • autoConfiguration도 Configuration중 하나에 해당한다.
    • spring.factories 내부에 여러 Configuration들이 있고 조건에 따라 Bean이 등록되게 되는데 메인 클래스 @SpringBootApplication을 실행하면 @EnableAutoConfiguration에 의해 spring.factories 안에 있는 수많은 자동 설정들이 조건에 따라 적용되어 수 많은 Bean들이 생성된다.
    • 간단하게 정리하면, Application Context를 만들 때 자동으로 빈설정이 되도록 하는 기능이다.
  • @Component
    • 개발자가 직접 작성한 class를 Bean으로 등록하기 위한 애노테이션
  • @Bean
    • 개발자가 직접 제어가 불가능한 외부 라이브러리등을 bean으로 만들려할 때 사용되는 애노테이션
  • @Configuration
    • @Configuration을 클래스에 적용하고 @Bean을 해당 class의 메서드에 적용하면 @autowired로 Bean을 부를 수 있다.
  • @Autowired
    • 스프링이 Type에 따라 알아서 Bean을 주입해준다.
    • Type을 먼저 확인한 후 못 찾으면 Name에 따라 주입한다.
    • 강제로 주입하고자 하는 경우 @Qulifier을 같이 명시
  • @Qualifier
    • 같은 타입의 빈이 두 개 이상 존재하는 경우 스프링이 어떤 빈을 주입해야할 지 알 수 없어서 스프링 컨테이너를 초기화하는 과정에서 예외가 발생한다.
    • @Qualifier는 @Autowired와 함께 사용하여 정확히 어떤 bean을 사용할지 지정하여 특정 의존 객체를 주입할 수 있다.
  • @Resource
    • @Autowired와 마찬가지로 Bean 객체를 주입해주는데 차이점은 Autowired는 타입으로, Resource는 이름으로 연결해준다.
    • 애노테이션 사용으로 인해 특정 Framework에 종속적인 애플리케이션을 구성하지 않기 위해서 @Resource 사용을 권장한다.
  • @Controller
    • API와 view를 동시에 사용하는 경우에 사용
    • 보통 view 화면 return을 목적으로 사용한다.
  • @RestController
    • view가 필요 없이 API만 지원하는 서비스에서 사용
  • @SpringBootApplication
    • @Configuration, @EnableAutoConfiguration, @ComponentScan 3가지를 하나로 합친 애노테이션

자료 참고
[참고 1] [참고 2]

'to Developer > Spring' 카테고리의 다른 글

Ajax  (0) 2024.02.14
WAS, Servlet  (0) 2024.02.01
Spring Framework  (1) 2024.01.29
Dispatcher Servlet  (0) 2024.01.26
MVC 패턴  (0) 2024.01.26