https://sundaland.tistory.com/42
IoC 컨테니어와 빈즈
Inversion of Control (IpC) 원칙의 스프링 프레임워크 구현에 대해 다룬다. 의존성 주입 (Dependency Injection)은 IoC의 특수한 형태로, 객체가 자신이 작업하는 다른 객체들을 생성자 아규먼트, 팩토리 머서드의 아규먼트, 또는 객체 인스턴스가 생성되거나 팩토리 메서드에서 리턴된 후에 설정되는 속성을 통해서만 정의하는 방법이다.
IoC 컨테이너는 빈(Bean)을 생성할 때 이러한 의존성을 주입한다. 이 과정은 본질적으로 빈 자체가 클래스의 직접 생성 또는 서비스 로케이터 패턴과 같은 메커니즘을 사용하여 자신의 의존성을 인스턴스화하거나 위치를 제어하는 것이 반대이다.
org.springframework.beans 및 org.springframework.context 패키지는 스프링 프레임워크의 IoC 컨테이너의 기반이 된다. BeanFactory 인터페이스는 모든 타입의 객체를 관리할 수 있는 고급 구성 메커니즘을 제공한다.
스프링 IoC 컨테이너는 BeanFactory 인터페이스를 구현한 구체이다. 일반적으론 사용되는 구현체는 ApplicationContext 이며, BeanFactory의 하위 인터페이스이다.
- 스프링의 AOP 기능과의 더 쉬운 통합
- 메시지 리소스 처리 (국제화에 사용)
- 이벤트 발행
- 웹 애플리케이션에서 사용하기 위한 WebApplicationContext와 같은 애플리케이션 계층별 컨텍스트
BeanFactory는 구성 프레임워크와 기본 기능을 제공하면, ApplicationContext는 더 많은 엔터프라이즈 관련 기능을 추가한다. ApplicationContext는 BeanFactroy의 완전한 상위 집합이다.
스프링에서는 애플리케이션의 중추를 형성하고 스프링 IoC 컨테이너에 의해 관리되는 객체를 Bean이라고 한다. Bean은 스프링 IoC 컨테이너에 의해 인스턴스화되고, 조립되고, 관리되는 객체이다. 그러지 않으면 Bean은 애플리케이션의 많은 객체 중 하나일 뿐이다. Bean과 그들 사이의 의존성은 컨테이너에서 사용되는 구성 메타 데이터에 반영된다.
컨테이너
org.springframeworkd.context.ApplicationContext 인터페이스는 Spring IoC 컨테이너를 나타내며, 빈의 인스턴스화, 구성, 조립을 담당한다. 컨테이너는 구성 메타데이터를 읽어 인스턴스화, 구성 및 조립할 구성 요소에 대한 지침을 받는다. 구성 메타데이터는 어노테이션이 달린 구성 컴포넌트 클래스, 팩토리 메서드가 있는 구성 클래스, 또는 외부 XML 파일이나 Groovy 스크립터로 표현될 수 있다.
이러한 형식 중 어느 것을 사용하더라도 애플리케이션과 구성 컴포넌트 간의 풍부한 상호 의존성을 구성할 수 있다.
ApplicationContext 인터페이스의 여러 구현들은 핵심 스프링의 일부이다. 독립 실행 (Stand-alone) 애플리케이션에서는 AnnotationConfigApplicationContext 또는 ClassPathXmlApplicationContext 인스턴스를 생성하는 것이 일반적이다.
대부분의 애플리케이션 시나리오에서는, 스프링 IoC 컨테이너의 한 개 이상의 인스턴스를 인스턴스화하는 명시적인 사용자 코드가 필요하지 않다.
스프링 부트 시나리오에서는 일반적인 설정 규칙 (Convention)에 따라 애플리케이션 컨텍스트가 자동으로 부트스트랩된다.
# POJO는 다이렉트 슈퍼 클래스로 Object만 상속할 수 있다.
Configuratuin Metadata
스프링 IoC 컨테이너는 구성 메타데이터 형태를 사용한다. 이 구성 메타데이터는 애플리케이션 개발자인 여러분이 스프링 컨테이너에게 애플리케이션의 구성 컴포넌트들을 인스턴스화, 구성 및 조립하는 방법을 지시하는 방식이다.
스프링 IoC 컨테이너 자체는 이 구성 메타데이터가 실제로 작성되는 형식과 완전히 분리(Decoupled)되어 있다. 요즘에는 많은 개발자가 스프링 애플리케이션에 대해 자바 기반 Configuration을 선택한다.
- Annotation-based configuration : 애플리케이션의 컴포넌트 클래스에 Annotation-based 구성 메타데이터를 사용하여 Bean을 정의한다.
- Java-based configuration : 자바 기반 configuration클래스를 사용하여 애플리케이션 클래스 외부에 Bean을 정의한다.
스프링 구성은 컨테이너가 관리해야하는 하나, 일반적으로 하나 이상의 Bean 정의로 구성된다. 자바 구성은 일반적으로 @Configuration 클래스 내에서 각각 하나의 Bean 정의에 해당하는 @Bean 어노테이션이 달린 메서드를 사용한다.
이러한 Bean 정의는 애플리케이션을 구성하는 실제 객체에 해당한다. 일반적으로 서비스 계층 객체, 저장소나 데이터 접근 객체 (DAO)와 같은 영속성 계층 객체, 웹 컨트롤러와 같은 프레젠테이션 객체, JPA EntityManagerFactroy, JMS 큐 등과 같은 인프라 객체를 정의한다.
일반적으로 도메인 객체와 같은 세분화된 객체는 컨테이너에서 구성하지 않는데, 이는 주로 저장소와 비즈니스 로직의 책임으로 도메인 객체를 생성하고 로드하기 때문이다.
Using the Container
ApplicationContext는 다양한 빈과 그들의 dependencies를 등록하고 유지할 수 있는 고급 팩토리 인터페이스이다. AppllicationContext 인터페이스에는 빈을 가져오는 몇 가지의 다른 메서드가 있지만, 이상적으로는 애플피케이션 코드가 이 메서드들을 사용해서는 안된다.
실제로 애플리케이션 코드는 getBean() 메서드를 호출하지 말아야하며, Spring API에 대한 의존성이 없어야 한다. 예를들어 Spring의 웹 프레임워크 통합은 컨트롤러 및 JSF 관리 빈과 같은 다양한 웹 프레임웤, 구성 요소에 대한 의존성 주입을 제공하여 메타데이터를 통해 특정 빈에 대한 의존성을 선언할 수 있게 한다.
Bean Overview
Spring IoC 컨테이너는 하나 의상의 빈을 관리한다. 이러한 빈은 컨테이너에 제공하는 구성 메타데이터로 생성된다.
컨테이너 내부에서 이러한 빈 정의는 BeanDefinition 객체로 표현되며, 이 객체는 다음 메타데이터(기타 정보 포함)을 포함한다.
- A package-qualified class name : 일반적으로 정의된 빈의 실제 구현 클래스
- 빈의 동작 구성을 나타내는 엘리먼트로, 빈이 컨테이너에서 어떻게 동작해야 하는지를 명시한다.
- 빈이 작업을 수행하는데 필요한 다른 빈에 대한 참조이다. 이러한 참조는 협력자 또는 dependencies라 한다.
- 새로 생성된 객체에 설정할 기타 구성 설정. (커넥션 풀을 관리하는 빈, 풀의 크기 제한, 사용할 커넥션 개수)
이 메타데이터는 각 빈 정의를 구성하는 속성(properties) 세트로 변환된다.
특정 빈을 생성하는 방법에 대한 정보를 포함하는 빈 정의 외에도, ApplicationContext 구현은 컨테니어 외부(사용자에 의해) 에서 생성된 기존 객체의 동록도 허용한다.
이는 getBeanFactory() 메서드를 통해 ApplicationContext의 BeanFactory에 접근하여 수행되며, 이 메서드는 DefaultListableBeanFactory 구현을 반환한다.
DefaultListableBeanFactory는 registerSingleton(...) 및 registerBeanDefinition(...) 메서드를 통해 이러한 등록을 지원한다. 그러나 일반적인 애플리케이션은 정규 빈 정의 메타데이터를 통해 정의된 빈만 작업한다.
▼ Bean을 사용하는 예제 코드
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
public class Main {
public static void main(String[] args) {
// ApplicationContext를 초기화하여 AppConfig 클래스를 로드합니다.
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 빈 정의로부터 빈을 가져옵니다.
MyBean myBean = context.getBean("myBean", MyBean.class);
myBean.doSomething();
// ApplicationContext의 BeanFactory를 가져옵니다.
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory)
((AnnotationConfigApplicationContext) context).getBeanFactory();
// 컨테이너 외부에서 생성된 객체를 등록합니다.
MyBean externalBean = new MyBean();
beanFactory.registerSingleton("externalBean", externalBean);
// 등록된 빈을 가져와 사용합니다.
MyBean retrievedBean = context.getBean("externalBean", MyBean.class);
retrievedBean.doSomething();
// 새로운 빈 정의를 등록합니다.
BeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClassName(MyBean.class.getName());
beanFactory.registerBeanDefinition("newBean", beanDefinition);
// 등록된 빈 정의로부터 빈을 가져와 사용합니다.
MyBean newBean = context.getBean("newBean", MyBean.class);
newBean.doSomething();
}
}
- ApplicationContext를 초기화하며 AppConfig 클래스를 로드한다.
- 빈 정의로부터 빈을 가져온다.
- ApplicationContext의 BeanFactroy를 가져온다.
- 컨테이너 외부에 생성된 객체를 registerSingleton 메서드를 사용하여 등록한다.
- 등록된 빈을 가져와 사용한다.
- 새로운 빈 정의를 registerBeanDefinition 메서드를 사용하여 등록한다.
- 등록된 빈 정의로부터 빈을 가져와 사용한다.
이렇게 자바 기반 구성 메타데이터를 사용하여 스프링 컨테이너를 설정하고 관리할 수 있다.
빈 메타데이터와 수동으로 제공된 싱글톤 인스턴스는 컨테이너가 자동 연결(autowiring) 및 기타 introspection(자체/외부 검사)단계 동안 이를 적절히 처리할 수 있도록 가능한 빨리 등록되어야 한다.
기존 메타데이터와 기존 싱글톤 인스턴스를 어느 정도 오버라이딩하는 것은 지원되지만, 런타임에 새로운 빈을 등록하는 것 (팩토리에 대한 실기한 액세스와 동시에)은 공식적으로 지원되지 않으며, 이는 동시 액세스 예외, 빈 컨테이너의 일관되지 않은 상태 또는 두 가지 모두를 초래할 수 있다.
Naming Beans
모든 빈은 하나 이상의 식별자를 가지고 있다. 이러한 식별자는 빈을 호스팅하는 컨테이너내에서 고유해야 한다. 빈은 보통 하나의 식별자만 가지지만, 필요에 따라 추가 식별자를 가질 수 있으며, 이러한 추가 식별자는 별칭 (Aliases)로 간주될 수 있다.
빈에 대해 이름이나 id를 제공할 필요는 없다. 이름이나 id를 명시적으로 제공하지 않으면 컨테이너가 해당 빈에 대해 고유한 이름을 생성한다. 그러나 ref 요소를 사용하거나 서비스 로케이터 스타일 조회를 통해 해당 빈을 이름으로 참조하려면, 이름을 제공해야 한다. 이름을 제공하지 않는 이유는 주로 내부 빈 사용과 협력 객체 자동 연결(Autowiring)과 관련이 있다.
Bean Naming Conventions
관례적으로 빈을 이름 짓는 경우 자바 인스턴스 필드 이름 명명 규칙을 따른다. 빈 이름은 소문자로 시작하여 이후에 카멜 케이스 방식으로 작성된다.
Bean의 이름을 일관되게 지정하면 구성을 더 쉽게 읽고 이해할 수 있다. 또한 Spring AOP를 사용하면 이름별로 관련된 Bean 집합에 Advice를 적용할 때 많은 도움이 된다.
classpath에서 컴포넌트 스캐닝을 통해 Spring은 앞서 설명한 규칙에 따라 명명되지 않은 컴포넌트에 대한 빈 이름을 생성한다. 기본적으로 간단한 클래스 이름을 취하고 첫문자를 소문자로 바꾼다. 그러나 두 개 이상의 문자가 있고 첫 번째 문자와 두 번째 문자가 모두 대문자인 (비정상적) 특수한 경우에는 원래 대소문자가 유지된다. 이는 java.beans.Introspector.decapitalize(Spiring이 여기서 사용하는)에 정의된 것과 동일한 규칙이다.
Aliasing a Bean outside the Bean Definition
Bean Definition 자체에서 id 속성으로 지정된 하나의 이름과 name 속성에 지정된 여러 이름의 조합을 사용하여 빈에 여러 이름을 제공할 수 있다. 이러한 이름은 동일한 빈에 대한 동등한 별칭이 될 수 있으며, 애플리케이션의 각 컴포넌트가 해당 컴포넌트에 특정한 빈 이름을 사용하여 공통 의존성을 참조할 수 있게 하는 등 일부 상황에서 유용하다.
그러나 빈이 실제로 정의된 곳에서 모든 별칭을 지정하는 것은 항상 적절한 것은 아니다. 때로는 다른 곳에 정의된 빈에 대해 별칭을 도입하는 것이 바람직할 때가 있다. 이는 보통 대규모 시스템에서 구성 설정이 각 하위 시스템별로 분리되어 각 하위 시스템이 자체 객체 정의 세트를 가지는 경우에 해당한다.
Java-configuration
자바 구성을 사용하는 경우 @Bean 어노테이션을 사용하여 별칭을 제공할 수 있다.
'스프링 프레임워크' 카테고리의 다른 글
The IoC Container [4] (0) | 2024.08.14 |
---|---|
The IoC Container (3) (0) | 2024.08.09 |
The IoC Container (2) (0) | 2024.08.09 |
메이븐 (Maven) Build System (0) | 2024.08.08 |
POJO (Plain Old Java Object) (0) | 2024.08.05 |