Bean Overview
Spring IoC 컨테이너는 하나 이상의 빈을 관리합니다. 이러한 빈은 컨테이너에 제공하는 구성 메타데이터(e.g., XML <bean/>)로 구성됩니다. 컨테이너 내에서 이러한 빈 정의(Bean definition)는 아래 메타데이터를 포함하는 BeanDefinition 개체로 표시됩니다.
- A package-qualified class name: 정의 중인 Bean의 실제 구현 클래스입니다.
- Bean이 컨테이너에서 어떻게 동작해야 하는지를 나타내는 Bean 동작 구성 요소(Bean begavioral configuration) 예를 들어, scope, **라이프사이클 콜백 등이 있습니다.
- bean이 작업을 수행하는 데 필요한 다른 bean에 대한 참조. 이러한 참조를 협력자(collaborators) 혹은 종속성(dependencies)라고 합니다.
- 새로 생성된 개체에 설정할 기타 구성 설정. 예를 들어, 풀의 크기 제한 또는 연결 풀을 관리하는 Bean에서 사용할 연결 수 (pool, connection pool은 DB 커넥션 풀 등을 뜻합니다.)
** 빈의 라이프사이클은 빈의 생성부터 소멸까지의 단계를 의미하며, 이 단계에서 특정한 동작이 필요한 경우 콜백 메서드를 정의하여 실행할 수 있습니다. 예를 들어 @PostConstruct나 @PreDestroy 등이 지원됩니다.
이 메타데이터는 각 빈 정의를 구성하는 속성 집합으로 변환됩니다. 다음 표에는 이러한 속성에 대해 설명합니다.
Naming Beans
모든 Bean에는 하나 이상의 식별자(identifiers)가 있습니다. 이러한 식별자는 Bean을 호스팅하는 컨테이너 내에서 고유해야 합니다. Bean에는 일반적으로 식별자가 하나만 있지만, 둘 이상을 사용할 수도 있으며 이 경우 추가 항목은 별칭(aliases)으로 간주될 수 있습니다.
XML 기반 configuration metadata에서 id 속성, name 속성 또는 둘 다를 사용하여 bean 식별자를 지정합니다. id 속성을 사용하면 정확히 하나의 id를 지정할 수 있습니다. 일반적으로 이러한 이름은 alphanumberic이지만, 특수 문자도 포함할 수 있습니다. bean에 대한 다른 별칭(aliasing)을 도입하려는 공우 이름(name) 속성에 쉼표 혹은 세미콜론 혹은 공백으로 구분하여 지정할 수 있습니다.
반드시 Bean의 `이름`이나 `id`를 제공할 필요는 없습니다. `이름`이나 `id`를 명시적으로 제공하지 않으면 컨테이너는 해당 빈에 대해 고유한 이름을 생성합니다. 그러나 ref 또는 service locator 스타일 조회를 사용하여 bean을 `이름`으로 참조하려면 반드시 `이름`을 제공해야 합니다. 이름을 제공하지 않을 때는 보통 inner beans 사용 혹은 autowiring collaborators의 경우입니다.
참고로 Bean 이름을 지정할 때 인스턴스 필드 이름에 대한 표준 Java 규악을 사용하는 것이 규칙입니다. 한마디로 카멜 케이스를 사용하라는 말입니다. (e.g., accountManager, accountService, userDao, loginController) Bean 이름을 일관되게 지정해야 구성을 더 쉽게 읽고 이해할 수 있으며, Spring AOP를 사용시 이름으로 관련된 빈 집합에 대해 어드바이스를 쉽게 적용할 수 있습니다.
Bean 외부에서 Bean 별칭 지정(Aliasing a Bean outside the Bean definition)
Bean definition에서 id 속성을 사용하여 한 개의 이름을 지정할 수 있으며, name 속성에서 다른 이름을 여러 개 지정할 수도 있습니다. 이러한 이름은 동일한 빈에 대한 동등한 별칭이 될 수 있으며, 각 구성 요소가 해당 구성 요소 자체에 특정한 빈 이름을 사용하여 공통 종속성을 참조할 수 있는 경우 유용합니다.
그러나 빈이 실제로 정의된 위치에서 모든 별칭을 지정하는 것이 항상 적절한 것은 아닙니다. 이러한 상황은 대규모 시스템에서 구성이 각 하위 시스템에 분리되고 각 하위 시스템이 고유한 객체 정의 세트를 갖는 경우에 흔히 발생하는 상황입니다. XML 기반 구성 메타데이터에서는 <alias/> 요소를 사용하여 이를 수행할 수 있습니다.
<alias name="fromName" alias="toName"/>
이 경우 fromName이라는 빈은 이 별칭 정의를 사용한 후 toName으로 참조될 수도 있습니다.
예를 들어, 하위 시스템 A에 대한 구성 메타데이터는 subsystemA-dataSource라는 이름으로 DataSource를 참조할 수 있습니다. 또한, 하위 시스템 B에 대한 구성 메타데이터는 subsystemB-dataSource라는 이름으로 DataSource를 참조할 수 있습니다. 이 두 하위 시스템을 모두 사용하는 기본 애플리케이션을 구성할 때 기본 애플리케이션은 myApp-dataSource라는 이름으로 DataSource를 참조합니다. 세 개의 이름이 모두 동일한 개체를 참조하도록 하려면 다음 별칭 정의를 구성 메타데이터에 추가할 수 있습니다.
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
이제 각 구성 요소와 기본 애플리케이션은 고유하고 다른 정의와 충돌하지 않도록 보장되는 이름을 통해 dataSource를 참조할 수 있지만, 동일한 bean을 참조합니다.
참고로 Java-configuration을 사용하는 경우 @Bean을 사용하여 별칭을 제공할 수 있습니다.
Bean 인스턴스화(Instantiating Beans)
빈 정의는 기본적으로 하나 이상의 객체를 생성하기 위한 레시피입니다. 컨테이너는 요청이 오면 명명된 빈에 대한 레시피(빈 정의)를 보고 해당 빈 정의에 의해 캡슐화된 구성 메타데이터를 사용하여 실체 개체를 생성(또는 획득)합니다.
XML-based configuration metadata를 사용하는 경우 <bean/> 요소의 cass 속성에 인스턴스화할 객체의 유형(또는 클래스)을 지정합니다. 이 클래스 속성(내부적으로 Beandefinition 인스턴스의 Class 속성)은 일반적으로 필수입니다. (예외에 대해서는 팩토리 메서드를 사용한 인스턴스화(아래에서 배웁니다.) 및 Bean definition상속을 참조해 주세요.) 다음 두 가지 방법 중 하나로 Class 속성을 사용할 수 있습니다.
- 컨테이너가 생성자를 호출하여 bean을 직접 생성하는 경우: `new` 연산자를 사용하는 Java 코드와 다소 동일합니다.
- 컨테이너가 클래스의 정적 팩토리 메서드를 호출하는 경우: 정적 팩토리 메서드가 포함된 클래스를 지정합니다. 정적 팩토리 메서드의 호출 결과로 반환되는 객체 유형은 호출된 정적 팩토리 메서드와 동일한 클래스일 수도 있고, 다른 클래스일 수도 있습니다.
중첩된 클래스의 이름
nested class에 대한 빈 정의를 구성하려는 경우 중첩 클래스의 이진 이름 또는 소스 이름을 사용할 수 있습니다. 예를 들어, com.example 패키지에 SomeThing이라는 클래스가 있고, 이 SomeThing 클래스에 OtherThing이라는 정적 중첩 클래스가 있는 경우 달러 기호($) 또는 점(.)으로 구분할 수 있습니다. 따라서 bean definition에서 class 속성 값은 `com.example.SomeThing$OtherTing` 혹은 `com.example.SomeThing.OtherThing`이 됩니다.
생성자를 사용한 인스턴스화
생성자 접근 방식으로 bean을 생성하면 모든 일반 클래스가 Spring에서 사용 가능하고 호환됩니다. 즉, 개발 중인 클래스는 특정 인터페이스를 구현하거나 특정 방식으로 코딩할 필요가 없습니다. 단순히 bean 클래스를 지정하는 것으로 충분합니다. 그러나, 특정 빈에 사용하는 IoC 유형에따라 기본(empty) 생성자가 필요할 수 있습니다.
Spring IoC 컨테이너는 관리하려는 거의 모든 클래스를 관리할 수 있습니다. 대부분의 Spring 사용자는 기본(no-argument) 생성자와 컨테이너의 속성을 따라 모델링된 적절한 setter 및 getter만 있는 실제 JavaBeans를 선호합니다. 그렇지만 Spring 컨테이너는 더 이국적인 non-bean-style의 클래스를 포함할 수도 있습니다. 예를 들어, JavaBean 사양을 완전히 준수하지 않는 레거시 connection pool도 Spring 컨테이너가 관리할 수 있습니다.
XML-based configuration meatadata를 사용하여 다음과 같이 Bean 클래스를 지정할 수 있습니다.
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
생성자에 인수를 제공하고 개체가 생성된 후 개체 인스턴스 속성을 설정하는 매커니즘에 대한 자세한 내용은 종속성 주입을 참조해 주세요.
객체를 생성하기 위해 호출되는 정적 팩토리 메서드를 포함하는 실제 클래스를 지정하기 위해 컨테이너가 빈을 생성하기 위해 클래스
정적 팩토리 메서드를 사용한 인스턴스화
정적 팩토리 메서드로 생성한 빈을 정의할 때 class 속성을 사용하여 정적 팩토리 메서드를 포함하는 클래스를 지정하고 `factory-method`라는 속성을 사용하여 팩토리 메서드 자체의 이름을 지정합니다. 나중에 설명하는 선택적 인수를 사용하여 이 메서드를 호출하는 활성 개체를 반환할 수 있어야 합니다. 이 개체는 나중에 생성자를 통해 생성된 것처럼 처리됩니다. 이러한 bean definition의 한 가지 용도는 레거시 코드에서 정적 팩토리를 호출하는 것입니다.
다음 bean definition은 팩토리 메서드를 호출하여 빈이 생성되도록 지정합니다. definition은 반환된 객체의 유형(class)을 지정하지 않고 오히려 팩토리 메서드를 포함하는 클래스를 지정합니다. 이 예제에서 createInstance() 메서드는 정적 메서드여야 합니다. 다음 예제에서는 팩토리 메서드를 지정하는 방법을 보여줍니다.
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
아래는 위에 있는 bean definition과 함께 작동하는 클래스를 보여줍니다.
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
팩터리 메서드에 인수를 제공하고 개체가 팩터리에서 반환된 후 개체 인스턴스 속성을 설정하는 매커니즘에 대한 자세한 내용은 세부 종속성 및 구성을 참조해 주세요.
인스턴스 팩터리 메서드를 사용한 인스턴스화
직전에서 배운 static factory method를 사용한 인스턴스화와 비슷합니다. 다만, 인스턴스 팩토리 메서드를 사용한 인스턴스화는 컨테이너에서 기존 빈의 non-static 메서드를 호출하여 새 빈을 생성합니다. 이 메커니즘을 사용하면 class 속성을 비워두고 factory-bean 속성에서 개체를 생성하기 위해 호출할 인스턴스 메서드를 포함하는 현재(또는 부모 또는 조상) 컨테이너의 빈 이름을 지정합니다. factory-method 속성으로 팩토리 메서드의 이름을 설정합니다.
다음 예제는 이러한 bean을 구성하는 방법을 보여줍니다.
<!-- createInstance()라는 메서드를 포함하는 팩토리 빈 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- 해당 로케이터 빈에 필요한 종속성을 주입합니다. -->
</bean>
<!-- 팩토리 빈을 통해 생성될 빈 -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
아래는 위에 있는 bean definition과 함께 동작하는 클래스를 보여줍니다.
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
다음 예제와 같이 하나의 팩토리 클래스는 둘 이상의 팩토리 메서드를 보유할 수도 있습니다.
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- 해당 로케이터 빈에 필요한 종속성을 주입합니다. -->
</bean>
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
<bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
아래는 위에 있는 bean definition과 함께 동작하는 클래스를 보여줍니다.
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
private static AccountService accountService = new AccountServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
public AccountService createAccountServiceInstance() {
return accountService;
}
}
이 접근 방식은 팩토리 빈 자체가 DI를 통해 관리 및 구성될 수 있음을 보여줍니다. 자세한 내용은 종속성 및 구성을 참조해 주세요.
https://docs.spring.io/spring-framework/reference/core/beans/definition.html#beans-beanname
'스프링' 카테고리의 다른 글
Spring IoC 컨테이너 - 1 (0) | 2023.07.05 |
---|