Light Blue Pointer
본문 바로가기
Developing/TIL(Develop)

Spring IoC Container, Bean

by Greedy 2024. 5. 6.

Spring Bean

Bean 은 Spring IoC container 에 의해 관리(생성,조립,관리)되는 객체이다.

Spring Framework의 중추이다.

스프링 IoC 컨테이너는 제어의 역전을 실현하기 위한 핵심 요소이다. 이 컨테이너는 애플리케이션의 객체를 생성하고 관리하며, 이를 통해 객체 간의 의존성을 관리한다. 여기서 제어의 역전은 객체의 생성과 생명주기를 개발자가 아니라 컨테이너에 위임하는 것을 의미한다.

Spring IoC Container

Inversion of Control (IoC) 원칙을 따라 응용 프로그램의 객체 생성, 관리 및 의존성 주입을 담당한다.

Dependency injection (DI) 는 IoC의 일종으로, 객체들이 그들의 의존성을 Constructor의 argumenet(혹은 Factory Method의 argument)로 주입받는 식으로만 형성하는 것을 일컫는다.

 

IoC Container는 Bean이라고 하는 객체들을 인스턴스화, 조립 및 관리한다.

Spring IoC Container가 없으면 Bean들은 그저 객체일 뿐이다.

Bean과 그들 사이의 dependency는 IoC Container에서 사용하는 구성 메타데이터에 반영된다.

 

IoC 컨테이너의 핵심 기능

  • Bean Configuration metadata를 읽고 이를 기반으로 Bean을 인스턴스화하고 구성한다.
  • Bean 간의 의존성을 해결하고 필요한 의존 객체를 주입하여 객체 간의 결합도를 줄인다.

IoC 컨테이너의 이점

  • 애플리케이션의 객체를 중앙 집중식으로 관리할 수 있다.
  • 코드의 유연성과 재사용성이 향상된다.
  • 테스트 용이성이 향상된다.
  • 설정의 분리로 인해 애플리케이션의 확장성과 유지보수성이 향상된다.

 

Spring IoC Container 는 크게 org.springframework.beans 와 org.springframework.context 의 두 가지 패키지로 분류된다.

BeanFactory Interface는 모든 객체에 대해 사용할 수 있는 추가적인 configuration 방법들을 제공한다.

 

ApplicationContext 는 다음과 같은 특징을 가진 BeanFactory 의 하위 인터페이스이다. 

  • Spring’s AOP 와 더 쉬운 호환
  • Message resource handling (for use in internationalization)
  • Event publication
  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

Container

org.springframework.context.ApplicationContext 인터페이스는 대표적인 Spring IoC container 이다.

Bean들을 instantiating, configuring, assembling 한다.

Container는 어떤 객체를 instantiate, configure, assemble 할지 configuration metadata를 읽어서 결정한다.

configuration metadata 는 XML, Java annotations, Java code로 표현되고, application을 이루는 객체들과 그 객체들 간의 종속성을 나타낸다.

 

 

Inversion of Control (IoC)

제어의 역전은 객체가 직접적으로 다른 객체에 종속되지 않으면서, 객체 간의 관계를 외부에서 정의하는 프로세스이다. 즉, 객체가 필요한 종속성을 직접 생성하지 않고 외부에서 주입받아 사용하는 것을 의미한다.

의존성 주입(Dependency Injection, DI)
객체 간의 의존 관계를 느슨하게 만들기 위한 디자인 패턴 객체가 직접 필요로 하는 의존 객체를 생성하거나 관리하지 않고, 외부에서 이를 주입받아 사용함으로써 객체 간의 결합도를 낮춘다. 주로 생성자 주입(Constructor Injection), 세터 주입(Setter Injection), 인터페이스 주입(Interface Injection)의 방법으로 이루어진다. 의존성 주입을 사용하면 소프트웨어의 유지보수성, 테스트 용이성, 재사용성을이 높아진다. 또한 객체 간의 결합도를 줄여 시스템의 유연성을 향상시키고, 코드의 가독성을 높이는 데에도 기여한다. Spring Framework와 같은 프레임워크는 의존성 주입을 지원하여 개발자가 객체 간의 의존 관계를 더 유연하게 관리할 수 있도록 도와준다.
// 음식 클래스
public class Food {
    private String name;
    private String type;
    private double price;

    public Food(String name, String type, double price) {
        this.name = name;
        this.type = type;
        this.price = price;
    }

    // 게터와 세터
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Food{" +
                "name='" + name + '\\'' +
                ", type='" + type + '\\'' +
                ", price=" + price +
                '}';
    }
}
// 레스토랑 클래스
public class Restaurant {
    private Food food;

    public Restaurant(Food food) {
        this.food = food;
    }

    // 게터와 세터
    public Food getFood() {
        return food;
    }

    public void setFood(Food food) {
        this.food = food;
    }

    @Override
    public String toString() {
        return "Restaurant{" +
                "food=" + food +
                '}';
    }
}

레스토랑이 음식을 주입받는다

Traditional Approach

생성자로 객체를 생성한다

  // 음식 객체 생성
        Food food = new Food("Pizza", "Italian", 12.99);

        // 레스토랑 객체 생성하고 음식 객체를 주입
        Restaurant restaurant = new Restaurant(food);

수십 개 또는 수백 개의 클래스가 있는 애플리케이션을 상상해 보자.

때로는 전체 애플리케이션에서 단일 클래스의 인스턴스를 공유하고, 때로는 각 사용 사례에 대해 별도의 객체가 필요할 것이다.

이러한 수많은 객체를 관리하는 것을 쉽게 만들어 주는 것이 제어의 역전이다.

컨테이너에 적절한 구성 메타데이터를 제공하면 객체가 직접 의존성을 구성하는 대신 IoC 컨테이너에서 의존성을 가져올 수 있다.

Bean Configuration

1. Restaurant 클래스에 @Component 주석을 단다

@Component
public class Restaurant {
    // 이 부분은 이전과 동일합니다.
}

2. IoC Container에 Bean metadata를 제공하는 Configuration 클래스를 만든다

이 Configuration 클래스는 Food 타입의 Bean을 생성한다.

@ComponentScan 으로 Restaurant 클래스를 포함하는 패키지에서 Bean을 찾을 수 있도록 한다.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackageClasses = Restaurant.class)
public class AppConfig {
    @Bean
    public Food getFood() {
        return new Food("Pizza", "Italian", 12.99);
    }
}

3. Bean을 정의한 Configuration Class를 사용하여 IoC Container를 구축한다

AnnotationConfigApplicationContext 클래스의 인스턴스가 필요하다

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        Restaurant restaurant = context.getBean(Restaurant.class);
        System.out.println("Restaurant Info:");
        System.out.println(restaurant);

        Food food = context.getBean(Food.class);
        System.out.println("Food Info:");
        System.out.println(food);
    }
}

이렇게 테스트해서 IoC Container가 Bean을 올바르게 생성하고 초기화했음을 확인한다

Bean Lifecycle

Bean Scope

Bean을 Singleton으로 설정할 때 주의할 점은 무엇인가요

Bean 생성 방법

Spring Bean이 Circular Dependency를 가질 때 발생할 수 있는 문제에 대해 설명해주세요