[Spring] Ioc & 스프링 컨테이너 & 스프링 Bean

손효재·2022년 4월 18일
0

Spring

목록 보기
1/3

IoC란??

제어의 역전(Inversion of Control)으로, 메서드나 객체의 호출작업에 대한 제어권을 개발자가 아닌, 외부에서 결정하도록 역전된 것을 의미한다.

ex) 프레임워크 vs 라이브러리
프레임워크 - 내가 작성한 코드를 프레임워크가 제어하며 대신 실행한다.
라이브러리 - 내가 작성한 코드로 직접 제어의 흐름을 담당한다.

IoC 장단점

객체의 의존성을 역전시켜 객체간의 결합도를 줄이고, 코드를 유연하게 만든다.
가독성, 코드중복 방지, 유지 보수의 편리가 가능하게 한다.
하지만, 제어 구조가 역전되어 이해하기 어려운 코드가 될 수 있다.

Application Context (스프링 컨테이너) - Spring IoC

Spring이 제어를 위임하여 모든 의존성 객체를 Spring 실행시점에 만들어주고, 필요한 곳에 주입한다.

스프링이 Bean을 생성하고 관리하는 빈 팩토리(Bean Factory)를 확장한 IoC 컨테이너이다.
스프링 컨테이너는 @Configuration 이 붙은 클래스를 파라미터로 받아서, 설정 정보로 사용한다.

스프링 컨테이너는 객체의 인스턴스를 싱글턴으로 관리하기 때문에 싱글턴 컨테이너라고도 한다.
이로인해, Bean들이 싱글턴 패턴의 특징을 가지게 된다.

스프링 빈(Spring Bean)이란?

스프링 컨테이너(Application Context)가 관리하는 자바 객체를 빈(Bean)이라고 한다.
@Bean 어노테이션을 가지는 메서드는 스프링 컨테이너에 스프링 빈으로 등록된다.

Bean 관련 메서드

  • 스프링구현체.getBeanDefinitionNames() - 스프링에 등록된 모든 빈 이름 조회
  • .getBean(빈이름, 타입) - 빈 이름으로 빈 객체(인스턴스) 조회 (빈이름생략가능)
  • .getBeanDefinition() - 빈에 각각에 대한 메타데이터 정보
  • .getRole() - 스프링이 내부에서 사용하는 빈 구분
    - ROLE_APPLICATION : 일반적으로 사용자가 정의한 빈
    - ROLE_INFRASTRUCTURE : 스프링이 내부에서 사용하는 빈
  • .getBeansOfType() 을 사용하면 해당 타입의 모든 빈을 조회할 수 있다

Bean 조회

* 조회 대상 스프링 빈이 없으면 예외 발생 : NoSuchBeanDefinitionException

  • 타입으로 조회시 같은 타입의 스프링 빈이 둘 이상이면 오류가 발생한다. 이때는 빈 이름을 지정하자.
  • 부모 타입으로 조회하면, 자식 타입도 함께 조회한다.
    → 모든 자바 객체의 최고 부모인 Object 타입으로 조회하면, 모든 스프링 빈을 조회한다.

스프링 컨테이너에 스프링 빈이 등록되는 과정

  1. Component Scan
    @ComponentScan으로 @Component가 붙어있는 모든 클래스의 인스턴스를 생성해 Bean으로 등록한다.
    * 컴포넌트 스캔은 탐색위치를 지정할 수 있다.(프로젝트 최상단 권장, SpringBootApplication 내에 포함됨)
    excludeFilters로 컴포넌트 스캔에서 제외할 대상 지정가능

  2. Bean 설정파일에 직접 Bean을 등록
    스프링 컨테이너가 @Configuration이 붙은 클래스를 설정 정보로 사용하면서 @Bean이 적용된 메서드를 빈으로 등록한다.

@Configuration 을 적용하지 않고, @Bean 만 적용하면 어떻게 될까?

스프링 컨테이너는 CGLIB이라는 바이트코드 조작 라이브러리를 사용하여 싱글톤을 보장해준다.
하지만, Bean만 적용하면 CGLIB 기술 없이 순수한 클래스로 스프링 빈에 등록된 것을 확인할 수 있다.
→ 스프링 빈으로 등록은 되지만, 싱글톤을 보장하지 않는다.

@Bean과 @Component의 차이점

Component는 클래스 레벨에서 사용되며,
일반적으로 개발자가 작성한 컨트롤이 가능한 클래스를 Bean으로 등록한다.

Bean은 메서드 레벨에서 사용되며,
개발자가 컨트롤이 불가능한 외부 라이브러리를 Bean으로 등록할 때 Configuration 클래스 내에 사용한다.

스프링 빈 객체를 왜 싱글턴으로 사용할까?

스프링이 주로 적용하는 대상이 Java 엔터프라이즈 기술을 사용하는 서버 환경이기 때문이다.

스프링이 처음 설계된 서버 환경이 트래픽이 많은 높은 성능이 요구되는 환경이었고, 하나의 요청을 처리하기 위해 다양한 객체가 계층구조로 이루어져 있어서, 새로운 객체 생성으로 인한 부하를 감당해야 했다.

의존 관계를 주입할 객체를 계속 생성하고 소멸한다면, 아무리 GC가 성능이 좋아졌다고 하더라도 부담이 된다.
그래서 Spring에서는 Bean들을 기본적으로 싱글턴(Singleton)으로 관리한다.

서버는 트래픽이 많은 환경인데 사용자 요청이 많아지게 되면, 요청마다 각 스레드마다 객체를 생성하기 때문에 비용이 매우 크기 때문에 싱글턴으로 관리한다.

싱글턴은 안티패턴이라고도 하지 않나?

디자인 패턴에서 싱글톤(Singleton) 패턴은 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 재사용 하는 형태를 말한다.

Java로 구현한 싱글턴 패턴 문제점

  • 의존 관계상 구체 클래스에 의존하여 DIP를 위반하고, OCP 원칙을 지키기 어려워 유연성이 떨어진다.
  • 테스트하기 어렵다.
    → 타입을 인터페이스로 정의하고 그 인터페이스를 구현해서 만든 싱글턴이 아니라면, 싱글턴 인스턴스를 Mock 객체로 대체하기 어렵고, 동적으로 객체를 주입하기도 어렵다.
  • 서버환경에서 싱글턴 객체가 1개만 생성됨을 보장하지 못한다.
    → 클래스 로더를 어떻게 구성하느냐에 따라, 여러 개의 JVM에 분산돼서 설치되는 경우
    → 리플렉션이나 역직렬화 시에 싱글턴이 깨질 수도 있기 때문에 반드시 보장한다고 볼 수 없다.
  • private 생성자를 사용하기 때문에 자식 클래스를 만들기 어렵다.
    → 싱글턴을 위해 private 생성자를 사용하기 때문에 상속을 사용할 수 없으며, 객체지향적이지 못한 static 필드와 메서드를 사용하여 전역상태를 만들 수 있어 바람직하지 못하다.

Spring의 싱글턴

객체의 생성을 스프링에 위임하여 싱글턴 패턴을 적용하지 않아도
스프링 컨테이너가 직접 Spring Bean을 싱글톤으로 관리하면서 기존 싱글턴 패턴의 단점이 사라진다.

  • 구체 클래스에 의존하지 않으며 객체지향적으로 개발가능하다.
  • 테스트하기 용이하다.
  • 프레임워크를 통해 싱글턴 객체가 1개만 생성됨을 보장한다.
  • private 생성자가 필요없어 상속이 가능하다.

스프링 빈은 기본적으로 싱글턴 스코프로 생성되어 스프링 컨테이너의 시작부터 종료까지 유지된다.

싱글턴이면 멀티스레드의 동시성 이슈가 발생할 수 있을텐데 어떻게 설계해야할까?

싱글턴으로 인해 하나의 인스턴스를 공유하기 때문에 싱글톤 객체는 상태를 유지(stateful)하게 설계하면 안된다.
스프링 빈은 내부에 상태정보를 갖지않는 무상태(stateless)로 설계해야 한다!

  • 특정 클라이언트에 의존적인 필드가 있으면 안된다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안된다!
  • 가급적 읽기만 가능해야 한다.
  • 필드 대신에 자바에서 공유되지 않는 지역변수, 파라미터, ThreadLocal 등을 사용해야 한다.

무상태로 설계한다면, 특정 상태를 계속 관리해야되는 경우에는 어떻게 해야하나요?

→ 동시성을 관리하기 위해 synchronized, ThreadLocal 등으로 격리공간을 갖출 수 있는 방법을 고려할 것 같다.

0개의 댓글