Loading...
Spring Framework Reference Documentation 7.0.2의 Bean Overview의 한국어 번역본입니다.
아래의 경우에 피드백에서 신고해주신다면 반영하겠습니다.
감사합니다 :)
Spring IoC 컨테이너는 하나 이상의 bean을 관리합니다. 이러한 bean들은 컨테이너에 제공하는 설정 메타데이터(예를 들어 XML <bean/> 정의 형태)로 생성됩니다.
컨테이너 자체 내에서, 이러한 bean 정의들은 BeanDefinition 객체로 표현되며, 이 객체는 (다른 정보들 외에) 다음과 같은 메타데이터를 포함합니다:
이 메타데이터는 각 bean 정의를 구성하는 프로퍼티 집합으로 변환됩니다. 다음 표는 이러한 프로퍼티를 설명합니다:
| Property | Explained in… |
|---|---|
| Class | Instantiating Beans |
| Name | Naming Beans |
| Scope | Bean Scopes |
| Constructor arguments | Dependency Injection |
| Properties | Dependency Injection |
| Autowiring mode | Autowiring Collaborators |
| Lazy initialization mode | Lazy-initialized Beans |
| Initialization method | Initialization Callbacks |
| Destruction method | Destruction Callbacks |
Table 1. The bean definition
특정 bean을 생성하는 방법에 대한 정보를 포함하는 bean 정의 외에도,
ApplicationContext 구현은 컨테이너 외부(사용자에 의해)에서 생성된 기존 객체의 registration도 허용합니다. 이는 getAutowireCapableBeanFactory() 메서드를 통해 ApplicationContext의 BeanFactory에 접근함으로써 수행되며, 이 메서드는 DefaultListableBeanFactory 구현을 반환합니다.
DefaultListableBeanFactory는 registerSingleton(..) 및 registerBeanDefinition(..) 메서드를 통해 이러한 registration을 지원합니다. 그러나 일반적인 애플리케이션은 일반적인 bean 정의 메타데이터를 통해 정의된 bean만을 사용합니다.
Bean 메타데이터와 수동으로 제공된 singleton 인스턴스는 컨테이너가 autowiring 및 기타 introspection 단계 동안 이를 적절히 판단할 수 있도록 가능한 한 빨리 등록되어야 합니다. 기존 메타데이터와 기존 singleton 인스턴스를 어느 정도까지 override하는 것은 지원되지만, runtime에 새 bean을 등록하는 것(factory에 대한 live access와 동시)에 대해서는 공식적으로 지원되지 않으며, concurrent access exception, bean 컨테이너의 불일치 상태 또는 둘 다를 초래할 수 있습니다.
Bean overriding은 이미 할당된 identifier를 사용하여 bean이 등록될 때 발생합니다. Bean overriding은 가능하지만, 설정을 읽기 어렵게 만듭니다.
Bean overriding은 향후 release에서 deprecated될 예정입니다.
Bean overriding을 완전히 비활성화하려면, ApplicationContext가 refresh되기 전에 allowBeanDefinitionOverriding flag를 false로 설정하면 됩니다. 이러한 설정에서는 bean overriding이 사용되면 exception이 발생합니다.
기본적으로, 컨테이너는 bean을 override하려는 모든 시도를 INFO level로 logging하므로, 이에 따라 설정을 조정할 수 있습니다. 권장되지는 않지만, allowBeanDefinitionOverriding flag를 true로 설정하여 이러한 log를 비활성화할 수 있습니다.
Java 설정을 사용하는 경우, 해당 @Bean 메서드는 그 @Bean 메서드의 return type이 해당 bean 클래스와 일치하는 한, 동일한 component name을 가진 scanned bean 클래스를 항상 조용히 override합니다. 이는 단순히 컨테이너가 bean 클래스에 미리 선언된 어떤 constructor보다 @Bean factory 메서드를 우선적으로 호출한다는 의미입니다.
Test scenario에서 bean을 override하는 것이 편리하다는 점을 인지하고 있으며, 이를 위한 명시적인 지원이 있습니다. 자세한 내용은 이 섹션을 참고하십시오.
모든 bean은 하나 이상의 identifier를 가집니다. 이러한 identifier는 bean을 host하는 컨테이너 내에서 고유해야 합니다. Bean은 일반적으로 하나의 identifier만 가집니다. 그러나 둘 이상이 필요한 경우, 추가 identifier는 alias로 간주될 수 있습니다.
XML 기반 설정 메타데이터에서, bean identifier를 지정하기 위해 id attribute, name attribute 또는 둘 다를 사용합니다. id attribute는 정확히 하나의 id를 지정할 수 있게 해줍니다. 관례적으로, 이러한 name은 영숫자('myBean', 'someService' 등)이지만, 특수 문자를 포함할 수도 있습니다.
Bean에 대한 다른 alias를 도입하려면, name attribute에 콤마(,), 세미콜론(;) 또는 공백으로 구분하여 지정할 수 있습니다. id attribute는 xsd:string type으로 정의되어 있지만, bean id의 고유성은 XML parser가 아니라 컨테이너에 의해 강제됩니다.
Bean에 대해 name이나 id를 제공할 필요는 없습니다. name이나 id를 명시적으로 제공하지 않으면, 컨테이너는 해당 bean에 대해 고유한 name을 생성합니다. 그러나 ref element 사용 또는 Service Locator style lookup을 통해 name으로 해당 bean을 참조하려면, name을 제공해야 합니다.
Name을 제공하지 않는 동기는 inner beans 및 autowiring collaborators를 사용하는 것과 관련이 있습니다.
Bean을 naming할 때에는 instance field name에 대한 표준 Java convention을 사용하는 것이 관례입니다. 즉, bean name은 소문자로 시작하고 그 이후에는 camel case를 사용합니다. 이러한 name의 예로는 accountManager, accountService, userDao, loginController 등이 있습니다.
Bean을 일관되게 naming하면 설정을 더 쉽게 읽고 이해할 수 있습니다. 또한 Spring AOP를 사용하는 경우, name으로 연관된 bean 집합에 advice를 적용할 때 큰 도움이 됩니다.
Classpath에서 component scanning을 사용할 때, Spring은 앞에서 설명한 규칙을 따라 name이 없는 component에 대해 bean name을 생성합니다. 본질적으로, simple class name을 가져와 첫 글자를 소문자로 바꿉니다. 그러나 (드문) 특수한 경우로, 두 글자 이상이면서 첫 번째와 두 번째 문자가 모두 대문자인 경우, 원래의 대소문자가 유지됩니다. 이는
java.beans.Introspector.decapitalize(Spring이 여기서 사용하는)의 규칙과 동일합니다.
Bean 정의 자체에서, id attribute에 의해 지정된 최대 하나의 name과 name attribute에 있는 임의 개수의 다른 name을 조합하여 bean에 대해 둘 이상의 name을 제공할 수 있습니다. 이러한 name은 동일한 bean에 대한 동등한 alias가 될 수 있으며, 애플리케이션의 각 component가 해당 component 자체에 특화된 bean name을 사용하여 공통 의존성을 참조하도록 하는 등의 상황에서 유용합니다.
그러나 bean이 실제로 정의된 위치에서 모든 alias를 지정하는 것만으로는 항상 충분하지는 않습니다. 다른 곳에 정의된 bean에 대해 alias를 도입하는 것이 바람직한 경우도 있습니다. 이는 일반적으로 각 subsystem이 자체 객체 정의 집합을 가지며 설정이 각 subsystem 간에 분할되는 대규모 system에서 흔히 발생합니다.
XML 기반 설정 메타데이터에서는 <alias/> element를 사용하여 이를 수행할 수 있습니다. 다음 예제는 이를 수행하는 방법을 보여줍니다:
1<alias name="fromName" alias="toName"/>
이 경우, (같은 컨테이너에 있는) fromName이라는 name의 bean은 이 alias 정의를 사용한 이후에는 toName으로도 참조될 수 있습니다.
예를 들어, subsystem A에 대한 설정 메타데이터는 subsystemA-dataSource라는 name으로 DataSource를 참조할 수 있습니다. Subsystem B에 대한 설정 메타데이터는 subsystemB-dataSource라는 name으로 DataSource를 참조할 수 있습니다. 이 두 subsystem을 사용하는 main 애플리케이션을 구성할 때, main 애플리케이션은 myApp-dataSource라는 name으로 DataSource를 참조합니다.
세 개의 name이 모두 동일한 객체를 참조하도록 하려면, 설정 메타데이터에 다음 alias 정의를 추가할 수 있습니다:
1<alias name="myApp-dataSource" alias="subsystemA-dataSource"/> 2<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
이제 각 component와 main 애플리케이션은 다른 정의와 충돌하지 않도록 보장되는 고유한 name(사실상 namespace를 생성)을 통해 dataSource를 참조할 수 있지만, 모두 동일한 bean을 참조합니다.
Java 설정을 사용하는 경우, @Bean 어노테이션을 사용하여 alias를 제공할 수 있습니다.
자세한 내용은 Using the @Bean Annotation을 참고하십시오.
Bean 정의는 본질적으로 하나 이상의 객체를 생성하기 위한 recipe입니다. 컨테이너는 요청을 받으면 지정된 bean에 대한 recipe를 확인하고, 해당 bean 정의에 캡슐화된 설정 메타데이터를 사용하여 실제 객체를 생성(또는 획득)합니다.
XML 기반 설정 메타데이터를 사용하는 경우, 인스턴스화할 객체의 type(class)을 <bean/> element의 class attribute에 지정합니다. 이 class attribute(내부적으로는 BeanDefinition 인스턴스의 Class 프로퍼티)는 일반적으로 필수입니다. (예외는 Instantiation by Using an Instance Factory Method 및 Bean Definition Inheritance를 참조하십시오.)
Class 프로퍼티는 다음 두 가지 방법 중 하나로 사용할 수 있습니다:
new 연산자를 사용하는 것과 어느 정도 동등합니다.static factory 메서드를 호출하여 bean을 생성할 때, 호출되는 static factory 메서드를 포함하는 실제 클래스를 지정합니다. static factory 메서드 호출에서 반환되는 객체 type은 동일한 클래스일 수도 있고 전혀 다른 클래스일 수도 있습니다.Nested class에 대한 bean 정의를 구성하려면, nested class의 binary name 또는 source name을 사용할 수 있습니다.
예를 들어, com.example 패키지에 SomeThing이라는 클래스가 있고, 이 SomeThing 클래스에 OtherThing이라는 static nested class가 있는 경우, 이 둘은 dollar sign($) 또는 dot(.)으로 구분할 수 있습니다. 따라서 bean 정의에서 class attribute의 값은 com.example.SomeThing$OtherThing 또는 com.example.SomeThing.OtherThing가 될 수 있습니다.
Constructor 방식을 통해 bean을 생성할 때, 모든 일반 클래스는 Spring에서 사용 가능하며 호환됩니다. 즉, 개발 중인 클래스는 특정 인터페이스를 구현하거나 특정 방식으로 코드를 작성할 필요가 없습니다. Bean 클래스를 지정하는 것만으로 충분해야 합니다.
그러나 해당 bean에 대해 사용하는 IoC type에 따라 default(empty) constructor가 필요할 수 있습니다.
Spring IoC 컨테이너는 관리하려는 거의 모든 클래스를 관리할 수 있습니다. 진정한 JavaBean 관리에만 제한되지 않습니다. 대부분의 Spring 사용자는 컨테이너의 프로퍼티를 모델링한 적절한 setter와 getter 및 default(no-argument) constructor만을 가진 실제 JavaBean을 선호합니다.
컨테이너에는 보다 특이한 non-bean-style 클래스도 포함될 수 있습니다. 예를 들어, JavaBean specification을 전혀 준수하지 않는 legacy connection pool을 사용해야 하는 경우에도 Spring은 이를 관리할 수 있습니다.
XML 기반 설정 메타데이터에서는 다음과 같이 bean 클래스를 지정할 수 있습니다:
1<bean id="exampleBean" class="examples.ExampleBean"/> 2 3<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
Constructor에 argument를 제공하는 메커니즘(필요한 경우)과 객체가 생성된 후 객체 인스턴스 프로퍼티를 설정하는 메커니즘에 대한 자세한 내용은 Injecting Dependencies를 참조하십시오.
Constructor argument의 경우, 컨테이너는 여러 개의 overloaded constructor 중에서 해당되는 constructor를 선택할 수 있습니다. 그렇다 하더라도, 모호성을 피하기 위해 constructor signature를 가능한 한 단순하게 유지하는 것이 좋습니다.
Static factory 메서드로 생성하는 bean을 정의할 때는, class attribute를 사용하여 static factory 메서드를 포함하는 클래스를 지정하고, factory-method라는 attribute를 사용하여 factory 메서드 자체의 name을 지정합니다. 이 메서드를 호출(나중에 설명하는 선택적 argument와 함께)하고 live 객체를 반환할 수 있어야 하며, 이 객체는 이후 constructor를 통해 생성된 것처럼 처리됩니다. 이러한 bean 정의의 한 가지 용도는 legacy 코드의 static factory를 호출하는 것입니다.
다음 bean 정의는 bean이 factory 메서드를 호출하여 생성됨을 지정합니다. 정의는 반환되는 객체의 type(class)을 지정하지 않고, 대신 factory 메서드를 포함하는 클래스를 지정합니다. 이 예제에서 createInstance() 메서드는 static 메서드여야 합니다. 다음 예제는 factory 메서드를 지정하는 방법을 보여줍니다:
1<bean id="clientService" 2 class="examples.ClientService" 3 factory-method="createInstance"/>
다음 예제는 앞선 bean 정의와 함께 동작하는 클래스를 보여줍니다:
1public class ClientService { 2 private static ClientService clientService = new ClientService(); 3 private ClientService() {} 4 5 public static ClientService createInstance() { 6 return clientService; 7 } 8}
1class ClientService private constructor() { 2 companion object { 3 private val clientService = ClientService() 4 @JvmStatic 5 fun createInstance() = clientService 6 } 7}
Factory 메서드에 (선택적) argument를 제공하는 메커니즘과 factory에서 객체 인스턴스가 반환된 후 객체 인스턴스 프로퍼티를 설정하는 메커니즘에 대한 자세한 내용은 Dependencies and Configuration in Detail를 참조하십시오.
Factory 메서드 argument의 경우, 컨테이너는 동일한 name을 가진 여러 overloaded 메서드 중에서 해당되는 메서드를 선택할 수 있습니다. 그렇다 하더라도, 모호성을 피하기 위해 factory 메서드 signature를 가능한 한 단순하게 유지하는 것이 좋습니다.
Factory 메서드 overloading에서 전형적으로 문제가 되는 경우는,
mock메서드의 많은 overload를 가진 Mockito입니다. 가능한 한 가장 구체적인mockvariant를 선택하십시오:1<bean id="clientService" class="org.mockito.Mockito" factory-method="mock"> 2 <constructor-arg type="java.lang.Class" value="examples.ClientService"/> 3 <constructor-arg type="java.lang.String" value="clientService"/> 4</bean>
static factory 메서드를 통한 인스턴스화와 유사하게, instance factory 메서드를 사용한 인스턴스화는 컨테이너의 기존 bean의 non-static 메서드를 호출하여 새 bean을 생성합니다.
이 메커니즘을 사용하려면, class attribute를 비워 두고, factory-bean attribute에 instance 메서드가 포함된 현재(또는 parent 또는 ancestor) 컨테이너의 bean name을 지정합니다. Factory 메서드 자체의 name은 factory-method attribute로 설정합니다. 다음 예제는 이러한 bean을 구성하는 방법을 보여줍니다:
1<!-- the factory bean, which contains a method called createClientServiceInstance() --> 2<bean id="serviceLocator" class="examples.DefaultServiceLocator"> 3 <!-- inject any dependencies required by this locator bean --> 4</bean> 5 6<!-- the bean to be created via the factory bean --> 7<bean id="clientService" 8 factory-bean="serviceLocator" 9 factory-method="createClientServiceInstance"/>
다음 예제는 이에 해당하는 클래스를 보여줍니다:
1public class DefaultServiceLocator { 2 3 private static ClientService clientService = new ClientServiceImpl(); 4 5 public ClientService createClientServiceInstance() { 6 return clientService; 7 } 8}
1class DefaultServiceLocator { 2 companion object { 3 private val clientService = ClientServiceImpl() 4 } 5 fun createClientServiceInstance(): ClientService { 6 return clientService 7 } 8}
하나의 factory 클래스는 다음 예제에서 보듯이 둘 이상의 factory 메서드를 가질 수도 있습니다:
1<bean id="serviceLocator" class="examples.DefaultServiceLocator"> 2 <!-- inject any dependencies required by this locator bean --> 3</bean> 4 5<bean id="clientService" 6 factory-bean="serviceLocator" 7 factory-method="createClientServiceInstance"/> 8 9<bean id="accountService" 10 factory-bean="serviceLocator" 11 factory-method="createAccountServiceInstance"/>
다음 예제는 이에 해당하는 클래스를 보여줍니다:
1public class DefaultServiceLocator { 2 3 private static ClientService clientService = new ClientServiceImpl(); 4 5 private static AccountService accountService = new AccountServiceImpl(); 6 7 public ClientService createClientServiceInstance() { 8 return clientService; 9 } 10 11 public AccountService createAccountServiceInstance() { 12 return accountService; 13 } 14}
1class DefaultServiceLocator { 2 companion object { 3 private val clientService = ClientServiceImpl() 4 private val accountService = AccountServiceImpl() 5 } 6 7 fun createClientServiceInstance(): ClientService { 8 return clientService 9 } 10 11 fun createAccountServiceInstance(): AccountService { 12 return accountService 13 } 14}
이 접근 방식은 factory bean 자체가 dependency injection(DI)을 통해 관리되고 구성될 수 있음을 보여줍니다. Dependencies and Configuration in Detail을 참조하십시오.
Spring documentation에서 "factory bean"은 Spring 컨테이너에 구성되어 instance 또는 static factory 메서드를 통해 객체를 생성하는 bean을 의미합니다. 이에 반해,
FactoryBean(대소문자에 주의)은 Spring 특유의FactoryBean구현 클래스를 의미합니다.
특정 bean의 runtime type을 결정하는 것은 단순하지 않습니다. Bean 메타데이터 정의에 지정된 클래스는 선언된 factory 메서드와 결합되거나, FactoryBean 클래스가 되어 bean의 runtime type이 달라질 수 있는 초기 클래스 reference일 뿐이거나, instance-level factory 메서드의 경우(factory-bean name을 통해 resolve되는) 전혀 설정되지 않을 수도 있습니다.
추가로, AOP proxying은 bean 인스턴스를 인터페이스 기반 proxy로 감쌀 수 있으며, 이 경우 target bean의 실제 type 노출은(구현된 인터페이스만) 제한적입니다.
특정 bean의 실제 runtime type을 알아내는 권장 방법은 지정된 bean name에 대해 BeanFactory.getType을 호출하는 것입니다. 이는 위의 모든 경우를 고려하며, 동일한 bean name에 대해 BeanFactory.getBean 호출이 반환하게 될 객체 type을 반환합니다.
Container Overview
Dependencies