[번역] Spring 공식문서 - The IoC Container

공혁준·2022년 3월 14일
0
post-thumbnail

Ref. Spring Framework Documentation v.5.3.16
📌 이 글에서는 공식문서의 The IoC Container 부분만 다룹니다.

Container Overview

  • org.springframework.context.ApplicationContext 인터페이스는 Spring IoC 컨테이너를 나타내며 빈의 인스턴스화, 구성 및 조합을 담당합니다. 컨테이너는 구성 메타데이터를 읽어 인스턴스화, 구성 및 어셈블할 개체에 대한 지침을 얻습니다. 구성 메타데이터는 XML, Java 주석 또는 Java 코드로 표시됩니다. 이를 통해 응용 프로그램을 구성하는 개체와 이러한 개체 간의 풍부한 상호 종속성을 표현할 수 있습니다.
  • ApplicationContext 인터페이스의 여러 구현이 Spring과 함께 제공됩니다. 독립 실행형 응용 프로그램에서는 ClassPathXmlApplicationContext 또는 FileSystemXmlApplicationContext의 인스턴스를 만드는 것이 일반적입니다.
  • 다음 다이어그램은 Spring이 작동하는 방식을 보여줍니다. 애플리케이션 클래스는 구성 메타데이터와 결합되어 ApplicationContext가 생성되고 초기화된 후 완전히 구성되고 실행 가능한 시스템 또는 애플리케이션을 갖게 됩니다.

Configuration Metadata

  • 앞의 다이어그램에서 볼 수 있듯이 Spring IoC 컨테이너는 구성 메타데이터 형식을 사용합니다. 이 구성 메타데이터는 애플리케이션 개발자로서 Spring 컨테이너에 애플리케이션의 개체를 인스턴스화, 구성 및 조합하도록 지시하는 방법을 나타냅니다.
  • 구성 메타데이터는 전통적으로 단순하고 직관적인 XML 형식으로 제공됩니다.
  • XML 기반 구성 메타데이터는 이러한 빈을 최상위 요소 내부의 요소로 구성합니다.
  • 이러한 빈 정의는 애플리케이션을 구성하는 실제 객체에 해당합니다.
  • 다음 예는 XML 기반 구성 메타데이터의 기본 구조를 보여줍니다.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="..." class="...">  
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <bean id="..." class="...">
        <!-- collaborators and configuration for this bean go here -->
    </bean>

    <!-- more bean definitions go here -->

</beans>

Bean Overview

  • Spring IoC 컨테이너는 하나 이상의 빈을 관리합니다. 이러한 빈은 컨테이너에 제공하는 구성 메타데이터로 생성됩니다(예: XML 정의 형식).
  • 컨테이너 자체 내에서 이러한 빈 정의는 다음 메타데이터를 포함하는 BeanDefinition 객체로 표시됩니다.
    • 패키지 수식 클래스 이름: 일반적으로 정의되는 빈의 실제 구현 클래스입니다.
    • Bean이 컨테이너에서 어떻게 동작해야 하는지를 나타내는 Bean 동작 구성 요소(범위, 수명 주기 콜백 등).
    • Bean이 작업을 수행하는 데 필요한 다른 Bean에 대한 참조입니다. 이러한 참조를 협력자 또는 종속성이라고도 합니다.
    • 새로 생성된 개체 에서 설정할 기타 구성 설정(예: 풀의 크기 제한 또는 연결 풀을 관리하는 Bean에서 사용할 연결 수).

Naming Beans

  • 모든 빈에는 하나 이상의 식별자가 있습니다. 이러한 식별자는 Bean을 호스팅하는 컨테이너 내에서 고유해야 합니다.
  • Bean에는 일반적으로 하나의 식별자만 있습니다. 그러나 둘 이상이 필요한 경우 추가 항목은 별칭으로 간주될 수 있습니다.
  • XML 기반 구성 메타데이터에서 id 속성, name 속성 또는 둘 다를 사용하여 빈 식별자를 지정합니다.
  • id 속성을 사용하면 정확히 하나의 id를 지정할 수 있습니다. 일반적으로 이러한 이름은 영어나 숫자('myBean', 'someService' 등)이지만 특수 문자도 포함할 수 있습니다.
  • 빈에 대한 다른 별칭을 도입하려는 경우 쉼표(,), 세미콜론(;) 또는 공백으로 구분하여 name 속성에 지정할 수도 있습니다.
  • Bean의 이름이나 id를 꼭 제공할 필요는 없습니다. 이름이나 id를 명시적으로 제공하지 않으면 컨테이너는 해당 빈에 대해 고유한 이름을 생성합니다.

Bean Naming Conventions

빈 이름은 소문자로 시작하고 카멜 케이스 표기법을 사용합니다. 이러한 이름의 예로는 accountManager, accountService, userDao, loginController 등이 있습니다.

Instantiating Beans

  • bean definition은 본질적으로 하나 이상의 객체를 생성하기 위한 레시피입니다. 컨테이너는 요청 시 명명된 빈에 대한 레시피를 살펴보고 해당 bean definition에 의해 캡슐화된 구성 메타데이터를 사용하여 실제 객체를 생성(또는 획득)합니다.

Instantiation with a Constructor

  • 생성자 접근 방식으로 Bean을 생성하면 모든 일반 클래스가 Spring에서 사용 가능하고 호환됩니다. 즉, 개발 중인 클래스는 특정 인터페이스를 구현하거나 특정 방식으로 코딩할 필요가 없습니다. Bean 클래스를 지정하기만 하면 충분합니다.
  • Spring IoC 컨테이너는 관리하고자 하는 거의 모든 클래스를 관리할 수 있습니다.
  • XML 기반 구성 메타데이터를 사용하여 다음과 같이 빈 클래스를 지정할 수 있습니다.
<bean id="exampleBean" class="examples.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

Instantiation with a Static Factory Method

  • 정적 팩토리 메소드로 생성한 Bean을 정의할 때 class 속성을 사용하여 정적 팩토리 메소드를 포함하는 클래스를 지정하고 factory-method라는 속성을 사용하여 팩토리 메소드 자체의 이름을 지정하십시오.
  • 이 개체는 이후에 생성자를 통해 생성된 것처럼 처리됩니다.
  • 다음 예는 factory-method 를 지정하는 방법을 보여줍니다. 이 예에서 createInstance()메서드는 정적 메서드여야 합니다.
<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

Instantiation by Using an Instance Factory Method

  • 정적 팩토리 메소드를 통한 인스턴스화와 유사하게 인스턴스 팩토리 메소드를 사용한 인스턴스화는 컨테이너에서 기존 Bean의 비정적 메소드를 호출하여 새 Bean을 생성합니다.
  • 이 메커니즘을 사용하려면 class속성을 비워두고 factory-bean 속성에서 객체를 생성하기 위해 호출될 인스턴스 메소드를 포함하는 현재(또는 상위 또는 상위) 컨테이너의 빈 이름을 지정하십시오.
  • factory-method 속성으로 팩토리 메서드 자체의 이름을 설정합니다.
  • 다음 예는 이러한 빈을 구성하는 방법을 보여줍니다.
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

Determining a Bean’s Runtime Type

  • 특정 Bean의 런타임 유형은 결정하기 쉽지 않습니다.
  • 특정 Bean의 실제 런타임 유형을 찾는 권장 방법은 지정된 Bean 이름에 대한 BeanFactory.getType 호출입니다.

Dependencies

  • 일반적인 엔터프라이즈 애플리케이션은 단일 객체(또는 Spring 용어로 bean)로 구성되지 않습니다.
  • 가장 단순한 응용 프로그램에도 최종 사용자가 일관된 응용 프로그램으로 보는 것을 제시하기 위해 함께 작동하는 몇 가지 개체가 있습니다.

Dependency Injection

  • 종속성 주입(DI)은 객체가 생성자 argument, factory method의 argument, factory method에서 인스턴스가 구성되거나 return된 후에 객체 인스턴스에 설정된 속성을 통해서만 객체의 종속성을 정의하는 프로세스입니다.
  • 클래스의 직접 구성 또는 Service Locator 패턴 같은 매커니즘을 사용하여 Bean이 종속되는 위치나 인스턴스화를 제어하기에, 제어의 반전(Inversion of Control)이라고 할 수 있습니다.
  • DI 원칙으로 코드가 더 깔끔해지고, 객체에 종속성이 제공될 때 decoupling(분리)이 더 효과적입니다.

Constructor-based Dependency Injection

  • constructor-based DI는 컨테이너가 각각 종속성을 나타내는 여러 인수로 생성자를 호출하여 수행됩니다.
  • 다음 예제에서는 생성자 주입으로만 종속성을 주입할 수 있는 클래스를 보여줍니다.
public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder
    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

Setter-based Dependency Injection

  • setter-based DI는 빈을 인스턴스화하기 위해 인수가 없는 생성자 또는 인수가 없는 정적 팩토리 메서드를 호출한 후 빈에서 setter 메서드를 호출하는 컨테이너에 의해 수행됩니다.
  • 다음 예제는 순수 setter 주입을 사용해야만 종속성 주입이 가능한 클래스를 보여줍니다.
public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on the MovieFinder
    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

Dependency Resolution Process

  • 컨테이너는 다음과 같이 bean dependency resolution을 수행합니다.
    • ApplicationContext는 모든 빈을 설명하는 구성 메타데이터로 생성되고 초기화됩니다. 구성 메타데이터는 XML, Java 코드 또는 주석으로 지정할 수 있습니다.
    • 각 빈의 종속성은 속성, 생성자 인수 또는 정적 팩토리 메서드에 대한 인수의 형태로 표현됩니다(일반 생성자 대신 사용하는 경우). 이러한 종속성은 Bean이 실제로 생성될 때 Bean에 제공됩니다.
    • 각 속성 또는 생성자 인수는 설정할 값의 실제 정의이거나 컨테이너의 다른 빈에 대한 참조입니다.
    • 각 속성 또는 생성자 인수는 지정된 format에서 해당 속성 또는 생성자 인수의 실제 type으로 변환됩니다. 기본적으로 Spring은 문자열 형식으로 제공된 값을 int, long, String, boolean 등과 같은 모든 내장 유형으로 변환할 수 있습니다.
  • Spring 컨테이너는 컨테이너가 생성될 때 각 빈의 구성을 검증합니다.

Bean Scopes

  • bean definition을 생성할 때 해당 bean definition에 의해 정의된 클래스의 실제 인스턴스를 생성하기 위한 레시피를 생성합니다.
  • bean definition이 레시피라는 생각은 중요합니다. 왜냐하면 클래스와 마찬가지로 단일 레시피에서 많은 객체 인스턴스를 생성할 수 있기 때문입니다.
  • 특정 bean definition에서 생성된 객체에 연결될 다양한 종속성 및 구성 값을 제어할 수 있을 뿐만 아니라 특정 bean definition에서 생성된 객체의 범위도 제어할 수 있습니다. 이 접근 방식은 강력하고 유연합니다.
  • Bean은 여러 scope 중 하나에 deploy되도록 정의할 수 있습니다.
  • Spring Framework는 6개의 scope를 지원하며 그 중 4개는 web-aware ApplicationContext를 사용하는 경우에만 사용할 수 있습니다.
  • 사용자 custom scope를 만들 수도 있습니다.
ScopeDescription
singleton(Default) 각 Spring IoC 컨테이너에 대한 단일 객체 인스턴스에 대한 단일 bean definition의 범위를 지정합니다.
prototype단일 bean definition의 범위를 임의의 수의 개체 인스턴스로 지정합니다.
request단일 bean definition의 범위를 단일 HTTP 요청의 수명 주기로 지정합니다. 즉, 각 HTTP 요청에는 단일 bean definition의 뒤에서 생성된 빈의 고유한 인스턴스가 있습니다. web-aware Spring ApplicationContext의 컨텍스트에서만 유효합니다.
session단일 bean definition의 범위를 HTTP Session의 수명 주기로 지정합니다. web-aware Spring ApplicationContext의 컨텍스트에서만 유효합니다.
application단일 bean definition의 범위를 ServletContext의 수명 주기로 지정합니다. web-aware Spring ApplicationContext의 컨텍스트에서만 유효합니다.
websocket단일 bean definition의 범위를 WebSocket의 수명 주기로 지정합니다. web-aware Spring ApplicationContext의 컨텍스트에서만 유효합니다.

The Singleton Scope

  • bean definition을 정의하고 singleton으로 scope를 지정하면 Spring IoC 컨테이너는 해당 bean definition에 의해 정의된 객체의 인스턴스를 정확히 하나만 생성합니다.
  • 이 단일 인스턴스는 이러한 싱글톤 빈의 캐시에 저장되며 해당 이름이 지정된 빈에 대한 모든 후속 요청 및 참조는 캐시된 개체를 반환합니다.
  • 다음 이미지는 싱글톤 범위의 작동 방식을 보여줍니다.
  • 단일 Spring 컨테이너의 특정 클래스에 대해 하나의 빈을 정의하면 Spring 컨테이너는 해당 bean definition에 의해 정의된 클래스의 인스턴스를 하나만 생성합니다.
  • singleton scope는 Spring의 default scope입니다

Annotation-based Container Configuration

  • XML 설정의 대안으로 바이트 코드 메타데이터에 의존하는 annotation 기반 configuration을 들 수 있다.
  • 개발자는 관련 클래스, 메서드 또는 필드 선언에 대한 annotation을 사용하여 클래스 자체에서 configuration을 구성합니다.

@Required

@Required 어노테이션은 다음 예제와 같이 빈 속성 setter 메서드에 적용됩니다.

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Required
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // ...
}
  • 이 어노테이션은 영향을 받는 빈 속성이 구성 시 bean definition의 명시적 속성 값을 통해 또는 autowiring을 통해 채워져야 함을 나타냅니다.
  • 영향을 받는 Bean 속성이 채워지지 않은 경우 컨테이너에서 예외가 발생합니다.
  • 이렇게 하면 나중에 NullPointerException 등을 방지하여 명시적인 실패를 허용합니다.
  • 컨테이너 외부에서 클래스를 사용하는 경우에도 필요한 참조 및 값이 적용됩니다.

@Autowired

다음 예제와 같이 @Autowired 어노테이션을 생성자에 적용할 수 있습니다.

public class MovieRecommender {

    private final CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}
  • Spring Framework 4.3부터 대상 빈이 시작할 생성자를 하나만 정의한다면 이러한 생성자에 @Autowired 어노테이션은 더 이상 필요하지 않습니다.
  • 그러나 여러 생성자를 사용할 수 있고 기본 생성자가 없는 경우 컨테이너에 어떤 것을 사용할지 지시하려면 생성자 중 하나 이상에 @Autowired 어노테이션을 달아야 합니다.

@Primary

  • 유형별 autowiring은 여러 후보로 이어질 수 있으므로 선택 프로세스를 더 많이 제어해야 하는 경우가 많습니다.
  • 이를 수행하는 한 가지 방법은 Spring은 @Primary 어노테이션을 사용하는 것입니다.
  • @Primary는 단일값 종속성에 autowiring 될 후보로 여러 빈이 존재할 경우 우선순위를 부여해야 함을 나타냅니다.
  • 후보 중 정확히 하나의 primary bean이 존재하는 경우 autowired 값이 됩니다.
@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}
public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog" primary="true">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

MovieRecommenderfirstMovieCatalog와 autowired 됩니다.

@Qualifier

  • 선택 프로세스를 더 많이 제어해야 하는 경우 Spring의 @Qualifier 어노테이션을 사용할 수 있습니다.
  • qualifier 값을 특정 인수와 연관시켜 유형 일치 세트를 좁혀 각 인수에 대해 특정 빈이 선택되도록 할 수 있습니다.
public class MovieRecommender {

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

@Resource

  • Spring은 JSR-250 @Resource 어노테이션(javax.annotation.Resource)을 사용한 injection도 지원합니다.
  • 이것은 Java EE의 일반적인 패턴입니다.
  • @Resourcename 속성을 사용합니다. 기본적으로 Spring은 해당 값을 주입할 빈 이름으로 해석합니다.
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

이름을 명시적으로 지정하지 않으면 기본 이름은 필드 이름 또는 setter 메서드에서 파생됩니다.

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

@Value

  • @Value는 일반적으로 외부 속성을 주입하는 데 사용됩니다.
@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

@PostConstruct and @PreDestroy

  • @PostConstruct
    • 객체의 초기화 단계입니다.
    • 객체가 생성된 후 별도의 초기화 작업을 위해 실행하는 메소드를 선언합니다.
    • @PostConstruct 어노테이션을 설정한 init 메소드는 WAS가 띄워질 때 실행됩니다.
  • @PreDestroy
    • 객체의 소멸 단계입니다.
    • Spring 컨테이너에서 Bean을 제거하기 전에 실행돼야 할 메소드에 사용하는 어노테이션입니다.
public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

Classpath Scanning and Managed Components

  • 클래스 경로를 스캔하여 후보 구성 요소를 암시적으로 감지하는 옵션에 대해 설명합니다.
  • 후보 구성 요소는 필터 기준과 일치하고 컨테이너에 등록된 해당 bean definition이 있는 클래스입니다.
  • 이렇게 하면 XML을 사용하여 빈 등록을 수행할 필요가 없습니다.
  • 대신 annotation(예: @Component), AspectJ 유형 표현식 또는 고유한 사용자 정의 필터 기준을 사용하여 컨테이너에 등록된 bean 정의가 있는 클래스를 선택할 수 있습니다.

Further Stereotype Annotations

  • @Repository 주석은 리포지토리(데이터 액세스 개체 또는 DAO라고도 함)의 역할 또는 스테레오타입을 수행하는 모든 클래스에 대한 마커입니다.
  • Spring은 @Component, @Service@Controller와 같은 추가 스테레오타입 주석을 제공합니다.
  • @Component모든 Spring 관리 구성 요소에 대한 일반 스테레오타입입니다.
  • @Repository, @Service@Controller는 보다 구체적인 사용 사례를 위한 @Component의 전문화입니다(각각 지속성, 서비스 및 프레젠테이션 계층에서).
  • 따라서 @Component로 구성 요소 클래스에 주석을 달 수 있지만 대신 @Repository, @Service 또는 @Controller로 주석을 추가하면 클래스가 도구로 처리하거나 측면과 연결하는 데 더 적합합니다.
profile
몰입을 즐기는 개발자입니다.

0개의 댓글