Dependencies and Configuration in Detail
이전 섹션에서 언급했듯이, 관리되는 다른 빈(협력자)에 대한 참조나 인라인으로 정의된 값으로 빈 속성과 생성자 아규먼트를 정의할 수 있습니다. Spring의 XML 기반 구성 메타데이터는 이 목적을 위해 <property/> 및 <constructor-arg/> 엘리먼트 내에 하위 엘리먼트 타입을 지원합니다.
Straight Values (Primitives, Strings, and so on)
<property/> 엘리먼트의 value 속성은 속성이나 생성자 아규먼트를 사람이 읽을 수 있는 문자열 표현으로 지정합니다. 스프링의 conversion service 는 이러한 값을 String에서 속성이나 아규먼트의 실제 타입으로 변환하는 데 사용됩니다. 다음 예시는 다양한 값이 설정되는 것을 보여줍니다:
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="misterkaoli"/>
</bean>
위 xml 코드는 자바 기반 구성 메타데이터에서는 다음과 같이 작성할 수 있습니다.
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(destroyMethod = "close")
public BasicDataSource myDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("misterkaoli");
return dataSource;
}
}
다음 예제에서는 더 간결한 XML 구성을 위해 p-namespace를 사용합니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="misterkaoli"/>
</beans>
앞서의 XML은 더 간결합니다. 그러나 IntelliJ IDEA나 Spring Tools for Eclipse와 같이 빈 정의를 생성할 때 자동 속성 완성을 지원하는 IDE를 사용하지 않는 한, 오타는 디자인 시간이 아닌 런타임에 발견됩니다. 이러한 IDE 지원은 매우 권장됩니다.
다음과 같이 java.util.Properties 인스턴스를 구성할 수도 있습니다.
<bean id="mappings"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
위 xml 코드는 자바 기반 구성 메타데이터에서는 다음과 같이 작성할 수 있습니다.
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import java.util.Properties;
@Configuration
public class AppConfig {
@Bean
public PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
Properties properties = new Properties();
properties.setProperty("jdbc.driver.className", "com.mysql.jdbc.Driver");
properties.setProperty("jdbc.url", "jdbc:mysql://localhost:3306/mydb");
configurer.setProperties(properties);
return configurer;
}
@Value("${jdbc.driver.className}")
private String driverClassName;
@Value("${jdbc.url}")
private String url;
@Bean(destroyMethod = "close")
public BasicDataSource myDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername("root");
dataSource.setPassword("misterkaoli");
return dataSource;
}
}
The idref element
The idref element is simply an error-proof way to pass the id (a string value - not a reference) of another bean in the container to a <constructor-arg/> or <property/> element. The following example shows how to use it:
idref 엘리먼트는 컨테이너의 다른 빈의 id(문자열 값 - 참조 아님)를 <constructor-arg/> 또는 <property/> 요소에 전달하는 오류 방지 방식일 뿐입니다. 다음 예는 사용 방법을 보여줍니다.
<bean id="theTargetBean" class="..."/>
<bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean"/>
</property>
</bean>
The preceding bean definition snippet is exactly equivalent (at runtime) to the following snippet:
앞의 빈 정의 조각은 다음 조각과 (런타임에) 정확히 동일합니다.
<bean id="theTargetBean" class="..." />
<bean id="client" class="...">
<property name="targetName" value="theTargetBean"/>
</bean>
The first form is preferable to the second, because using the idref tag lets the container validate at deployment time that the referenced, named bean actually exists. In the second variation, no validation is performed on the value that is passed to the targetName property of the client bean. Typos are only discovered (with most likely fatal results) when the client bean is actually instantiated. If the client bean is a prototype bean, this typo and the resulting exception may only be discovered long after the container is deployed.
첫 번째 형태는 두 번째 형태보다 더 바람직한데, idref 태그를 사용하면 컨테이너가 배포 시점에 참조하게 하고 명명된 빈이 실제로 존재하는지 검증할 수 있기 때문입니다. 두 번째 변형에서는 클라이언트 빈의 targetName 속성에 전달된 값에 대한 검증이 수행되지 않습니다. 오타는 클라이언트 빈이 실제로 인스턴스화될 때만 발견됩니다(치명적 결과가 발생할 가능성이 높음). 클라이언트 빈이 프로토타입 빈인 경우 이 오타와 결과 예외는 컨테이너가 배포된 후 오랜 시간이 지난 후에야 발견될 수 있습니다.
A common place (at least in versions earlier than Spring 2.0) where the <idref/> element brings value is in the configuration of AOP interceptors in a ProxyFactoryBean bean definition. Using <idref/> elements when you specify the interceptor names prevents you from misspelling an interceptor ID.
<idref/> 엘리먼트가 값을 가져오는 일반적인 장소(적어도 Spring 2.0 이전 버전에서는)는 ProxyFactoryBean 빈 정의의 AOP 인터셉터 구성입니다. 인터셉터 이름을 지정할 때 <idref/> 요소를 사용하면 인터셉터 ID를 잘못 철자하는 것을 방지할 수 있습니다.
Java 기반 구성에서 idref 사용
idref 엘리먼트는 다른 빈의 ID를 안전하게 전달하는 방법으로 사용됩니다. 이를 Java 기반 구성으로 변환하면 다음과 같이 작성할 수 있습니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public TargetBean theTargetBean() {
return new TargetBean();
}
@Bean
public ClientBean theClientBean() {
ClientBean client = new ClientBean();
client.setTargetName("theTargetBean"); // 직접 ID를 설정
return client;
}
}
이 코드에서 setTargetName("theTargetBean")을 사용해 theTargetBean의 ID를 ClientBean에 전달합니다. 이 방법은 XML의 <idref> 요소와 동일한 역할을 하며, 타이핑 오류를 방지할 수 있습니다. Spring은 런타임에 지정된 ID가 실제로 존재하는지 확인합니다.
References to Other Beans (Collaborators)
The ref element is the final element inside a <constructor-arg/> or <property/> definition element. Here, you set the value of the specified property of a bean to be a reference to another bean (a collaborator) managed by the container. The referenced bean is a dependency of the bean whose property is to be set, and it is initialized on demand as needed before the property is set. (If the collaborator is a singleton bean, it may already be initialized by the container.) All references are ultimately a reference to another object. Scoping and validation depend on whether you specify the ID or name of the other object through the bean or parent attribute.
ref 엘리먼트는 <constructor-arg/> 또는 <property/> 정의 엘리먼트 내부의 마지막 엘리먼트입니다. 여기서 빈의 지정된 속성 값을 컨테이너에서 관리하는 다른 빈(협력자)에 대한 참조로 설정합니다. 참조된 빈은 속성을 설정할 빈의 종속성이며 속성이 설정되기 전에 필요에 따라 필요에 따라 초기화됩니다. (협력자가 싱글톤 빈인 경우 컨테이너에서 이미 초기화되었을 수 있습니다.) 모든 참조는 궁극적으로 다른 객체에 대한 참조입니다. 스코프 지정 및 검증은 빈 또는 부모 속성을 통해 다른 객체의 ID 또는 이름을 지정하는지 여부에 따라 달라집니다.
Specifying the target bean through the bean attribute of the <ref/> tag is the most general form and allows creation of a reference to any bean in the same container or parent container, regardless of whether it is in the same XML file. The value of the bean attribute may be the same as the id attribute of the target bean or be the same as one of the values in the name attribute of the target bean. The following example shows how to use a ref element:
<ref/> 태그의 bean 속성을 통해 타겟 bean을 지정하는 것이 가장 일반적인 형태이며, 동일한 XML 파일에 있는지 여부에 관계없이 동일한 컨테이너 또는 부모 컨테이너에 있는 모든 bean에 대한 참조를 생성할 수 있습니다. bean 속성의 값은 타겟 bean의 id 속성과 같거나 대상 bean의 name 속성에 있는 값 중 하나와 같을 수 있습니다. 다음 예는 ref 요소를 사용하는 방법을 보여줍니다.
<ref bean="someBean"/>
Specifying the target bean through the parent attribute creates a reference to a bean that is in a parent container of the current container. The value of the parent attribute may be the same as either the id attribute of the target bean or one of the values in the name attribute of the target bean. The target bean must be in a parent container of the current one. You should use this bean reference variant mainly when you have a hierarchy of containers and you want to wrap an existing bean in a parent container with a proxy that has the same name as the parent bean. The following pair of listings shows how to use the parent attribute:
parent 속성을 통해 타겟 빈을 지정하면 현재 컨테이너의 부모 컨테이너에 있는 빈에 대한 참조가 생성됩니다. parent 속성의 값은 대상 빈의 id 속성이나 타겟 빈의 name 속성에 있는 값 중 하나와 같을 수 있습니다. 타겟 빈은 현재 빈의 부모 컨테이너에 있어야 합니다. 이 빈 참조 변형은 주로 컨테이너 계층이 있고 부모 빈과 이름이 같은 프록시가 있는 부모 컨테이너에 기존 빈을 래핑하려는 경우에 사용해야 합니다. 다음 쌍의 목록은 parent 속성을 사용하는 방법을 보여줍니다.
<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService">
<!-- insert dependencies as required here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
</bean>
The local attribute on the ref element is no longer supported in the 4.0 beans XSD, since it does not provide value over a regular bean reference any more. Change your existing ref local references to ref bean when upgrading to the 4.0 schema.
Java 기반 구성에서 다른 빈에 대한 참조 설정
Spring의 Java 기반 구성에서 다른 빈에 대한 참조는 `@Bean` 메서드를 통해 이루어집니다. `ref` 요소의 역할을 Java 코드로 변환하면 다음과 같습니다:
1. 다른 빈에 대한 참조 설정
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public SomeBean someBean() {
return new SomeBean();
}
@Bean
public AnotherBean anotherBean() {
AnotherBean anotherBean = new AnotherBean();
anotherBean.setDependency(someBean()); // ref bean="someBean"
return anotherBean;
}
}
이 예제에서 anotherBean의 setDependency 메서드는 someBean 빈에 대한 참조를 설정합니다.
2. 부모 컨테이너의 빈 참조 설정 (예제)
부모 컨테이너의 빈을 참조하는 경우, 일반적으로 Spring Boot 또는 Spring의 여러 ApplicationContext를 사용하는 경우가 해당됩니다. 자바 기반 구성 메타데이터에서 동일한 컨텍스트 구조를 만들려면 부모와 자식 컨텍스트를 별도로 설정하고 구성해야 합니다. 다만, 자바 기반 구성에서 직접적으로 부모 컨테이너의 빈을 참조하는 방법은 특별한 설정이 필요하며, 대부분의 경우 자식 컨텍스트에서 부모의 빈을 자동으로 참조할 수 있습니다.
여기서는 일반적인 경우를 예시로 보여드리겠습니다:
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext parentContext = new AnnotationConfigApplicationContext(ParentConfig.class);
AnnotationConfigApplicationContext childContext = new AnnotationConfigApplicationContext(ChildConfig.class);
childContext.setParent(parentContext);
AccountService accountService = childContext.getBean("accountService", AccountService.class);
// childContext에서 accountService 빈 사용
}
}
@Configuration
public class ParentConfig {
@Bean
public AccountService accountService() {
return new SimpleAccountService();
}
}
@Configuration
public class ChildConfig {
@Bean
public AccountService accountService() {
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
proxyFactoryBean.setTarget(accountService()); // 부모 컨텍스트의 빈 참조
return (AccountService) proxyFactoryBean.getObject();
}
}
이 예제에서 childContext는 parentContext의 accountService 빈을 참조하여 설정됩니다. Java 기반 구성에서 부모 컨텍스트의 빈을 참조할 때, childContext.setParent(parentContext)를 사용하여 부모-자식 컨텍스트 관계를 설정합니다.
Inner Beans
A <bean/> element inside the <property/> or <constructor-arg/> elements defines an inner bean, as the following example shows:
다음 예제와 같이 <property/> 또는 <constructor-arg/> 요소 내의 <bean/> 요소는 내부 빈을 정의합니다.
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
An inner bean definition does not require a defined ID or name. If specified, the container does not use such a value as an identifier. The container also ignores the scope flag on creation, because inner beans are always anonymous and are always created with the outer bean. It is not possible to access inner beans independently or to inject them into collaborating beans other than into the enclosing bean.
내부 빈 정의에는 정의된 ID나 이름이 필요하지 않습니다. 지정된 경우 컨테이너는 이러한 값을 식별자로 사용하지 않습니다. 컨테이너는 또한 생성 시 범위 플래그를 무시합니다. 내부 빈은 항상 익명이며 항상 외부 빈과 함께 생성되기 때문입니다. 내부 빈에 독립적으로 액세스하거나 둘러싼 빈이 아닌 협력하는 빈에 주입할 수 없습니다.
As a corner case, it is possible to receive destruction callbacks from a custom scope — for example, for a request-scoped inner bean contained within a singleton bean. The creation of the inner bean instance is tied to its containing bean, but destruction callbacks let it participate in the request scope’s lifecycle. This is not a common scenario. Inner beans typically simply share their containing bean’s scope.
코너 케이스로, 사용자 정의 범위에서 destruction 콜백을 수신하는 것이 가능합니다. 예를 들어, 싱글톤 빈에 포함된 요청 범위의 내부 빈의 경우입니다. 내부 빈 인스턴스의 생성은 포함하는 빈에 연결되어 있지만 파괴 콜백을 통해 요청 범위의 라이프사이클에 참여할 수 있습니다. 이는 일반적인 시나리오가 아닙니다. 내부 빈은 일반적으로 포함하는 빈의 범위를 공유합니다.
Java 기반 구성에서 Inner Bean을 정의하는 방법을 살펴보겠습니다. Inner Bean은 다른 빈의 속성이나 생성자 인수로 사용되며, 독립적으로 접근할 수 없는 익명 빈입니다.
Java 기반 구성으로 변환
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public OuterBean outer() {
OuterBean outerBean = new OuterBean();
outerBean.setTarget(new Person("Fiona Apple", 25)); // Inner Bean 정의
return outerBean;
}
}
설명
- Person 빈은 OuterBean의 속성으로 정의되며, 직접 접근이 불가능한 Inner Bean입니다.
- Inner Bean은 익명으로 생성되며, 외부에서 참조할 수 없고, 부모 빈의 생명주기를 따릅니다.
Collections
The <list/>, <set/>, <map/>, and <props/> elements set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively. The following example shows how to use them:
<list/>, <set/>, <map/>, <props/> 엘리먼트는 Java Collection 타입 List, Set, Map, Properties의 속성과 아규먼트를 각각 설정합니다. 다음 예제는 이를 사용하는 방법을 보여줍니다.
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
The value of a map key or value, or a set value, can also be any of the following elements:
map 키나 값의 값, 혹은 set 값은 다음 엘리먼트 중 하나가 될 수도 있습니다.
bean | ref | idref | list | set | map | props | value | null
Collection Merging
The Spring container also supports merging collections. An application developer can define a parent <list/>, <map/>, <set/> or <props/> element and have child <list/>, <map/>, <set/> or <props/> elements inherit and override values from the parent collection. That is, the child collection’s values are the result of merging the elements of the parent and child collections, with the child’s collection elements overriding values specified in the parent collection.
Spring 컨테이너는 컬렉션 병합도 지원합니다. 애플리케이션 개발자는 부모 <list/>, <map/>, <set/> 또는 <props/> 엘리먼트를 정의하고 자식 <list/>, <map/>, <set/> 또는 <props/> 엘리먼트가 부모 컬렉션에서 값을 상속하고 오버라이드하도록 할 수 있습니다. 즉, 자식 컬렉션의 값은 부모 및 자식 컬렉션의 엘리먼트를 병합한 결과이며, 자식 컬렉션 엘리먼트는 부모 컬렉션에 지정된 값을 오버라이드합니다.
This section on merging discusses the parent-child bean mechanism. Readers unfamiliar with parent and child bean definitions may wish to read the relevant section before continuing.
The following example demonstrates collection merging:
병합에 대한 이 섹션에서는 부모-자식 빈 메커니즘을 설명합니다. 부모 및 자식 빈 정의에 익숙하지 않은 독자는 계속하기 전에 relevant section 을 읽어보는 것이 좋습니다.
다음 예는 컬렉션 병합을 보여줍니다.
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge="true">
<prop key="sales">sales@example.com</prop>
<prop key="support">support@example.co.uk</prop>
</props>
</property>
</bean>
<beans>
Notice the use of the merge=true attribute on the <props/> element of the adminEmails property of the child bean definition. When the child bean is resolved and instantiated by the container, the resulting instance has an adminEmails Properties collection that contains the result of merging the child’s adminEmails collection with the parent’s adminEmails collection. The following listing shows the result:
administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk
The child Properties collection’s value set inherits all property elements from the parent <props/>, and the child’s value for the support value overrides the value in the parent collection.
자식 Properties 컬렉션의 값 집합은 부모 <props/>에서 모든 속성 엘리먼트를 상속하고, 지원 값에 대한 자식의 값은 부모 컬렉션의 값을 오버라이드합니다.
This merging behavior applies similarly to the <list/>, <map/>, and <set/> collection types. In the specific case of the <list/> element, the semantics associated with the List collection type (that is, the notion of an ordered collection of values) is maintained. The parent’s values precede all of the child list’s values. In the case of the Map, Set, and Properties collection types, no ordering exists. Hence, no ordering semantics are in effect for the collection types that underlie the associated Map, Set, and Properties implementation types that the container uses internally.
이 병합 동작은 <list/>, <map/> 및 <set/> 컬렉션 타입에도 유사하게 적용됩니다. <list/> 엘리먼트의 특정한 경우 List 컬렉션 타입과 관련된 시맨틱(즉, 값의 정렬된 컬렉션 개념)이 유지됩니다. 부모의 값은 모든 자식 리스트의 값보다 우선합니다. Map, Set 및 Properties 컬렉션 타입의 경우 정렬이 없습니다. 따라서 컨테이너가 내부적으로 사용하는 연관된 Map, Set 및 Properties 구현 타입의 기반이 되는 컬렉션 타입에는 정렬 의미가 적용되지 않습니다.
Limitations of Collection Merging
You cannot merge different collection types (such as a Map and a List). If you do attempt to do so, an appropriate Exception is thrown. The merge attribute must be specified on the lower, inherited, child definition. Specifying the merge attribute on a parent collection definition is redundant and does not result in the desired merging.
여러분은 다른 컬렉션 타입(예: Map 및 List)을 병합할 수 없습니다. 병합을 시도하면 적절한 예외가 발생합니다. 병합 속성은 하위 상속된 자식 정의에 지정해야 합니다. 부모 컬렉션 정의에 병합 속성을 지정하는 것은 중복이며 원하는 병합을 수행하지 못합니다.
Strongly-typed collection
Thanks to Java’s support for generic types, you can use strongly typed collections. That is, it is possible to declare a Collection type such that it can only contain (for example) String elements. If you use Spring to dependency-inject a strongly-typed Collection into a bean, you can take advantage of Spring’s type-conversion support such that the elements of your strongly-typed Collection instances are converted to the appropriate type prior to being added to the Collection. The following Java class and bean definition show how to do so:
Java의 제너릭 타입 지원 덕분에 강력한 타입의 컬렉션을 사용할 수 있습니다. 즉, (예를 들어) String 엘리먼트들만 포함할 수 있도록 Collection 타입을 선언할 수 있습니다. Spring을 사용하여 강력한 타입의 Collection을 빈에 종속성 주입하는 경우 Spring의 타입 변환 지원을 활용하여 강력한 타입의 Collection 인스턴스의 엘리먼트가 Collection에 추가되기 전에 적절한 타입으로 변환됩니다. 다음 Java 클래스와 빈 정의는 이를 수행하는 방법을 보여줍니다.
public class SomeClass {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="something" class="x.y.SomeClass">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
When the accounts property of the something bean is prepared for injection, the generics information about the element type of the strongly-typed Map<String, Float> is available by reflection. Thus, Spring’s type conversion infrastructure recognizes the various value elements as being of type Float, and the string values (9.99, 2.75, and 3.99) are converted into an actual Float type.
something 빈의 accounts 속성이 주입을 위해 준비되면, 강력한 타입의 Map<String, Float>의 엘리먼트 타입에 대한 제네릭 정보를 리플렉션을 통해 사용할 수 있습니다. 따라서 Spring의 타입 변환 인프라는 다양한 값 엘리먼트를 Float 타입으로 인식하고 문자열 값(9.99, 2.75 및 3.99)은 실제 Float 타입으로 변환됩니다.
Java 기반 구성에서 컬렉션 다루기
Spring XML에서 컬렉션(list, set, map, props)을 사용하는 방법을 Java 기반으로 변환한 예제입니다.
1. 컬렉션 사용 예제
public class ComplexObject {
private Properties adminEmails;
private List<Object> someList;
private Map<String, Object> someMap;
private Set<Object> someSet;
// Getters and Setters for each property
public void setAdminEmails(Properties adminEmails) {
this.adminEmails = adminEmails;
}
public void setSomeList(List<Object> someList) {
this.someList = someList;
}
public void setSomeMap(Map<String, Object> someMap) {
this.someMap = someMap;
}
public void setSomeSet(Set<Object> someSet) {
this.someSet = someSet;
}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.*;
@Configuration
public class AppConfig {
@Bean
public ComplexObject moreComplexObject() {
ComplexObject complexObject = new ComplexObject();
Properties adminEmails = new Properties();
adminEmails.put("administrator", "administrator@example.org");
adminEmails.put("support", "support@example.org");
adminEmails.put("development", "development@example.org");
complexObject.setAdminEmails(adminEmails);
List<Object> someList = new ArrayList<>();
someList.add("a list element followed by a reference");
someList.add(myDataSource());
complexObject.setSomeList(someList);
Map<String, Object> someMap = new HashMap<>();
someMap.put("an entry", "just some string");
someMap.put("a ref", myDataSource());
complexObject.setSomeMap(someMap);
Set<Object> someSet = new HashSet<>();
someSet.add("just some string");
someSet.add(myDataSource());
complexObject.setSomeSet(someSet);
return complexObject;
}
@Bean
public MyDataSource myDataSource() {
return new MyDataSource();
}
}
2. 컬렉션 병합 예제
컬렉션 병합을 Java 기반으로 구현하는 예제를 보여줍니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import java.util.Properties;
@Configuration
public class AppConfig {
@Bean
public ComplexObject parent() {
ComplexObject parent = new ComplexObject();
Properties adminEmails = new Properties();
adminEmails.put("administrator", "administrator@example.com");
adminEmails.put("support", "support@example.com");
parent.setAdminEmails(adminEmails);
return parent;
}
@Bean
public ComplexObject child() {
GenericBeanDefinition childDefinition = new GenericBeanDefinition();
childDefinition.setParentName("parent");
childDefinition.setBeanClass(ComplexObject.class);
ComplexObject child = new ComplexObject();
Properties adminEmails = new Properties();
adminEmails.put("sales", "sales@example.com");
adminEmails.put("support", "support@example.co.uk"); // overriding
child.setAdminEmails(adminEmails);
return child;
}
}
3. 강력한 타입의 컬렉션 예제
Java의 제네릭 타입을 사용하는 강력한 타입의 컬렉션을 구성하는 방법입니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class AppConfig {
@Bean
public SomeClass something() {
SomeClass someClass = new SomeClass();
Map<String, Float> accounts = new HashMap<>();
accounts.put("one", 9.99f);
accounts.put("two", 2.75f);
accounts.put("six", 3.99f);
someClass.setAccounts(accounts);
return someClass;
}
}
이 예제들은 컬렉션을 사용하여 다양한 방법으로 Spring에서 빈을 구성하고 의존성을 주입하는 방법을 Java 기반으로 설명합니다.
Null and Empty String Values
Spring treats empty arguments for properties and the like as empty Strings. The following XML-based configuration metadata snippet sets the email property to the empty String value ("").
Spring은 속성 등에 대한 빈 아규먼트를 빈 문자열로 처리합니다. 다음 XML 기반 구성 메타데이터 스니펫은 email 속성을 빈 문자열 값("")으로 설정합니다.
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
The preceding example is equivalent to the following Java code:
앞의 예제는 다음 Java 코드와 동일합니다.
exampleBean.setEmail("");
The <null/> element handles null values. The following listing shows an example:
<null/> 요소는 null 값을 처리합니다. 다음 목록은 예를 보여줍니다.
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
The preceding configuration is equivalent to the following Java code:
이전 구성은 다음 Java 코드와 동일합니다.
exampleBean.setEmail(null);
XML Shortcut with the p-namespace
The p-namespace lets you use the bean element’s attributes (instead of nested <property/> elements) to describe your property values collaborating beans, or both.
Spring supports extensible configuration formats with namespaces, which are based on an XML Schema definition. The beans configuration format discussed in this chapter is defined in an XML Schema document. However, the p-namespace is not defined in an XSD file and exists only in the core of Spring.
The following example shows two XML snippets (the first uses standard XML format and the second uses the p-namespace) that resolve to the same result:
p- namespace 를 사용하면 중첩된 <property/> 엘리먼트 대신 빈 엘리먼트의 속성을 사용하여 속성 값 협업 빈 또는 둘 다를 설명할 수 있습니다.
Spring은 XML 스키마 정의를 기반으로 하는 네임스페이스를 사용하여 확장 가능한 구성 형식을 지원합니다. 이 장에서 설명하는 빈 구성 형식은 XML 스키마 문서에 정의되어 있습니다. 그러나 p-네임스페이스는 XSD 파일에 정의되어 있지 않으며 Spring의 핵심에만 존재합니다.
다음 예제는 동일한 결과로 해결되는 두 개의 XML 스니펫(첫 번째는 표준 XML 형식을 사용하고 두 번째는 p-네임스페이스를 사용함)을 보여줍니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="classic" class="com.example.ExampleBean">
<property name="email" value="someone@somewhere.com"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="someone@somewhere.com"/>
</beans>
The example shows an attribute in the p-namespace called email in the bean definition. This tells Spring to include a property declaration. As previously mentioned, the p-namespace does not have a schema definition, so you can set the name of the attribute to the property name.
이 예에서는 빈 정의에서 email이라는 p-네임스페이스의 속성을 보여줍니다. 이것은 Spring에 속성 선언을 포함하라고 알려줍니다. 이전에 언급했듯이, p-네임스페이스에는 스키마 정의가 없으므로 속성 이름을 속성 이름으로 설정할 수 있습니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ExampleBean classic() {
ExampleBean exampleBean = new ExampleBean();
exampleBean.setEmail("someone@somewhere.com");
return exampleBean;
}
@Bean
public ExampleBean pNamespace() {
ExampleBean exampleBean = new ExampleBean();
exampleBean.setEmail("someone@somewhere.com");
return exampleBean;
}
}
This next example includes two more bean definitions that both have a reference to another bean:
다음 예제에는 다른 빈에 대한 참조를 갖는 두 개의 빈 정의가 더 포함되어 있습니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
This example includes not only a property value using the p-namespace but also uses a special format to declare property references. Whereas the first bean definition uses <property name="spouse" ref="jane"/> to create a reference from bean john to bean jane, the second bean definition uses p:spouse-ref="jane" as an attribute to do the exact same thing. In this case, spouse is the property name, whereas the -ref part indicates that this is not a straight value but rather a reference to another bean.
이 예제에는 p-네임스페이스를 사용하는 속성 값뿐만 아니라 속성 참조를 선언하기 위해 특수 형식도 사용됩니다. 첫 번째 빈 정의는 <property name="spouse" ref="jane"/>을 사용하여 빈 john에서 빈 jane으로의 참조를 생성하는 반면, 두 번째 빈 정의는 p:spouse-ref="jane"을 속성으로 사용하여 정확히 동일한 작업을 수행합니다. 이 경우 spouse는 속성 이름이고 -ref 부분은 이것이 직접적인 값이 아니라 다른 빈에 대한 참조임을 나타냅니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public Person jane() {
Person jane = new Person();
jane.setName("Jane Doe");
return jane;
}
@Bean
public Person johnClassic() {
Person john = new Person();
john.setName("John Doe");
john.setSpouse(jane()); // jane 빈에 대한 참조 설정
return john;
}
@Bean
public Person johnModern() {
Person john = new Person();
john.setName("John Doe");
john.setSpouse(jane()); // jane 빈에 대한 참조 설정
return john;
}
}
The p-namespace is not as flexible as the standard XML format. For example, the format for declaring property references clashes with properties that end in Ref, whereas the standard XML format does not. We recommend that you choose your approach carefully and communicate this to your team members to avoid producing XML documents that use all three approaches at the same time.
p-네임스페이스는 표준 XML 형식만큼 유연하지 않습니다. 예를 들어, 속성 참조를 선언하는 형식은 Ref로 끝나는 속성과 충돌하지만 표준 XML 형식은 그렇지 않습니다. 세 가지 접근 방식을 모두 동시에 사용하는 XML 문서를 생성하지 않으려면 접근 방식을 신중하게 선택하고 팀원에게 이를 전달하는 것이 좋습니다.
XML Shortcut with the c-namespace
Similar to the XML Shortcut with the p-namespace, the c-namespace, introduced in Spring 3.1, allows inlined attributes for configuring the constructor arguments rather then nested constructor-arg elements.
The following example uses the c: namespace to do the same thing as the from Constructor-based Dependency Injection:
다음 예제는 c: 네임스페이스를 사용하여 from Constructor-based Dependency Injection과 동일한 작업을 수행합니다.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- traditional declaration with optional argument names -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="something@somewhere.com"/>
</bean>
<!-- c-namespace declaration with argument names -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>
The c: namespace uses the same conventions as the p: one (a trailing -ref for bean references) for setting the constructor arguments by their names. Similarly, it needs to be declared in the XML file even though it is not defined in an XSD schema (it exists inside the Spring core).
c: 네임스페이스는 p:와 동일한 규칙을 사용합니다(빈 참조에 대한 후행 -ref). 생성자 아규먼트를 이름으로 설정하는 데 사용합니다. 마찬가지로 XSD 스키마에 정의되어 있지 않더라도 XML 파일에 선언해야 합니다(Spring 코어 내부에 존재함).
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ThingTwo beanTwo() {
return new ThingTwo();
}
@Bean
public ThingThree beanThree() {
return new ThingThree();
}
// 생성자 기반 의존성 주입을 사용하는 beanOne 빈 정의
@Bean
public ThingOne beanOne() {
return new ThingOne(beanTwo(), beanThree(), "something@somewhere.com");
}
}
For the rare cases where the constructor argument names are not available (usually if the bytecode was compiled without debugging information), you can use fallback to the argument indexes, as follows:
생성자 아규먼트 이름을 사용할 수 없는 드문 경우(일반적으로 바이트코드가 디버깅 정보 없이 컴파일된 경우) 다음과 같이 아규먼트 인덱스로의 대체 방법을 사용할 수 있습니다.
<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
c:_2="something@somewhere.com"/>
Due to the XML grammar, the index notation requires the presence of the leading _, as XML attribute names cannot start with a number (even though some IDEs allow it). A corresponding index notation is also available for <constructor-arg> elements but not commonly used since the plain order of declaration is usually sufficient there.
XML 문법 때문에 인덱스 표기법은 선행 _가 필요합니다. XML 속성 이름은 숫자로 시작할 수 없기 때문입니다(일부 IDE에서는 허용하지만). 해당 인덱스 표기법은 <constructor-arg> 요소에도 사용할 수 있지만, 선언의 일반 순서로 충분하기 때문에 일반적으로 사용되지 않습니다.
In practice, the constructor resolution mechanism is quite efficient in matching arguments, so unless you really need to, we recommend using the name notation throughout your configuration.
실제로, 생성자 해결 메커니즘은 아규먼트를 일치시키는 데 매우 효율적이므로, 정말로 필요하지 않다면 구성 전체에서 이름 표기법을 사용하는 것이 좋습니다.
Compound Property Names
You can use compound or nested property names when you set bean properties, as long as all components of the path except the final property name are not null. Consider the following bean definition:
경로의 모든 구성 요소가 최종 속성 이름을 제외하고 null이 아닌 한, 빈 속성을 설정할 때 복합 또는 중첩된 속성 이름을 사용할 수 있습니다. 다음 빈 정의를 고려하세요.
<bean id="something" class="things.ThingOne">
<property name="fred.bob.sammy" value="123" />
</bean>
The something bean has a fred property, which has a bob property, which has a sammy property, and that final sammy property is being set to a value of 123. In order for this to work, the fred property of something and the bob property of fred must not be null after the bean is constructed. Otherwise, a NullPointerException is thrown.
something 빈에는 fred 속성이 있고, fred 속성에는 bob 속성이 있고, bob 속성에는 sammy 속성이 있으며, 마지막 sammy 속성은 123 값으로 설정됩니다. 이것이 작동하려면, something의 fred 속성과 fred의 bob 속성은 빈이 생성된 후에 null이 아니어야 합니다. 그렇지 않으면 NullPointerException이 throw됩니다.
위의 XML 기반 빈 정의를 자바 기반 구성 메타데이터로 변환해 보겠습니다. 아래 예제는 ThingOne 클래스가 fred라는 프로퍼티를 가지고 있고, 이 fred가 bob이라는 프로퍼티를, 그리고 bob이 sammy라는 프로퍼티를 가진다고 가정합니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public ThingOne something() {
ThingOne thingOne = new ThingOne();
Fred fred = new Fred();
Bob bob = new Bob();
bob.setSammy(123); // bob의 sammy 프로퍼티를 123으로 설정
fred.setBob(bob); // fred의 bob 프로퍼티를 설정
thingOne.setFred(fred); // thingOne의 fred 프로퍼티를 설정
return thingOne;
}
}
위 코드에서 ThingOne, Fred, Bob 클래스는 각각 다음과 같이 정의되어야 합니다:
public class ThingOne {
private Fred fred;
public Fred getFred() {
return fred;
}
public void setFred(Fred fred) {
this.fred = fred;
}
}
public class Fred {
private Bob bob;
public Bob getBob() {
return bob;
}
public void setBob(Bob bob) {
this.bob = bob;
}
}
public class Bob {
private int sammy;
public int getSammy() {
return sammy;
}
public void setSammy(int sammy) {
this.sammy = sammy;
}
}
이 구성 메타데이터는 XML에서 정의한 것과 동일한 효과를 가집니다. ThingOne 빈이 생성되면 fred와 bob 프로퍼티가 올바르게 초기화되고, sammy는 123으로 설정됩니다. 이 설정은 스프링 컨테이너에서 빈을 초기화할 때 NPE가 발생하지 않도록 보장합니다.
Using depends-on
Spring의 Java 기반 구성에서는 @DependsOn 어노테이션을 사용하여 빈 간의 초기화 순서를 지정할 수 있습니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
@Configuration
public class AppConfig {
@Bean
@DependsOn("manager")
public ExampleBean beanOne() {
ExampleBean exampleBean = new ExampleBean();
exampleBean.setManager(manager());
return exampleBean;
}
@Bean
public ManagerBean manager() {
return new ManagerBean();
}
@Bean
public JdbcAccountDao accountDao() {
return new JdbcAccountDao();
}
@Bean
@DependsOn({"manager", "accountDao"})
public ExampleBean beanOneWithMultipleDependencies() {
ExampleBean exampleBean = new ExampleBean();
exampleBean.setManager(manager());
return exampleBean;
}
}
위 코드에서:
- @DependsOn("manager")를 사용하여 beanOne이 manager 빈이 먼저 초기화된 후에 초기화되도록 지정합니다.
- @DependsOn({"manager", "accountDao"})를 사용하여 beanOneWithMultipleDependencies가 manager와 accountDao 빈이 모두 초기화된 후에 초기화되도록 지정합니다.
Lazy-initialized Beans
@Lazy 어노테이션을 사용하여 lazy-initialized 빈을 정의할 수 있습니다. 또한, 컨테이너 수준에서 기본적으로 모든 빈을 lazy-initialized로 설정하려면 @Configuration 클래스에 @Lazy 어노테이션을 추가할 수 있습니다.
개별 빈에 대한 lazy initialization
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
public class AppConfig {
@Bean
@Lazy
public ExpensiveToCreateBean lazyBean() {
return new ExpensiveToCreateBean();
}
@Bean
public AnotherBean notLazyBean() {
return new AnotherBean();
}
}
위 코드에서:
- @Lazy 어노테이션을 lazyBean 메서드에 추가하여 해당 빈이 처음 요청될 때까지 초기화되지 않도록 설정합니다.
- notLazyBean은 별도의 어노테이션이 없기 때문에, 기본적으로 즉시 초기화됩니다.
컨테이너 수준의 lazy initialization
모든 빈을 lazy-initialized로 설정하려면, @Configuration 클래스에 @Lazy 어노테이션을 추가할 수 있습니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@Configuration
@Lazy
public class AppConfig {
@Bean
public ExpensiveToCreateBean lazyBean() {
return new ExpensiveToCreateBean();
}
@Bean
public AnotherBean notLazyBean() {
return new AnotherBean();
}
}
위 코드에서는 @Lazy 어노테이션이 @Configuration 클래스 레벨에 적용되어, 이 클래스 내의 모든 빈이 lazy-initialized 됩니다.
Autowiring Collaborators
생략...
Method Injection
대부분의 애플리케이션 시나리오에서, 컨테이너 내의 대부분의 빈들은 싱글톤입니다. 싱글톤 빈이 다른 싱글톤 빈과 협력해야 하거나 비싱글톤 빈이 다른 비싱글톤 빈과 협력해야 할 때, 일반적으로 다른 하나의 빈을 속성으로 정의함으로써 의존성을 처리합니다. 문제는 빈 생명주기가 다를 때 발생합니다. 싱글톤 빈 A가 비싱글톤(프로토타입) 빈 B를 사용해야 한다고 가정해 보겠습니다. 컨테이너는 싱글톤 빈 A를 단 한 번만 생성하므로 속성을 설정할 기회도 한 번뿐입니다. 컨테이너는 필요할 때마다 빈 A에 새로운 빈 B 인스턴스를 제공할 수 없습니다.
해결책은 일부 제어의 역전을 포기하는 것입니다. ApplicationContextAware 인터페이스를 구현하여 빈 A가 컨테이너를 인식하게 하고, 빈 A가 필요할 때마다 컨테이너에 getBean("B") 호출을 하여 (일반적으로 새로운) 빈 B 인스턴스를 요청할 수 있습니다. 다음 예시는 이 접근 방법을 보여줍니다:
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* A class that uses a stateful Command-style class to perform
* some processing.
*/
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
package fiona.apple;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import java.util.Map;
// Command interface
interface Command {
void setState(Map<String, Object> state);
Object execute();
}
// Concrete Command implementation
class ConcreteCommand implements Command {
private Map<String, Object> state;
@Override
public void setState(Map<String, Object> state) {
this.state = state;
}
@Override
public Object execute() {
// Perform some operation based on the state
return "Command executed with state: " + state;
}
}
// CommandManager class
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map<String, Object> commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
return this.applicationContext.getBean("command", Command.class);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
// Main method to run the example
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
CommandManager commandManager = context.getBean(CommandManager.class);
// Creating a sample state map
Map<String, Object> state = Map.of("key1", "value1", "key2", "value2");
// Processing the command
Object result = commandManager.process(state);
System.out.println(result);
}
}
// Spring Configuration class
@Configuration
@ComponentScan("fiona.apple")
class AppConfig {
@Bean
public Command command() {
return new ConcreteCommand();
}
@Bean
public CommandManager commandManager() {
return new CommandManager();
}
}
앞서 언급된 방법은 바람직하지 않습니다. 왜냐하면 비즈니스 코드가 Spring 프레임워크를 인지하고 이에 종속되기 때문입니다. Spring IoC 컨테이너의 다소 고급 기능인 메소드 주입은 이러한 사용 사례를 깔끔하게 처리할 수 있게 합니다.
Lookup Method Injection
Lookup method injection은 컨테이너가 컨테이너 관리 빈의 메소드를 오버라이드하고 컨테이너 내의 다른 이름이 지정된 빈에 대한 조회 결과를 반환하는 능력입니다. 조회는 일반적으로 앞서 설명된 시나리오에서처럼 프로토타입 빈을 포함합니다. Spring 프레임워크는 CGLIB 라이브러리의 바이트코드 생성을 사용하여 메소드를 오버라이드하는 동적 서브클래스를 생성함으로써 이 메소드 인젝션을 구현합니다.
⦁ 이 동적 서브클래싱이 작동하려면, Spring 빈 컨테이너가 서브클래싱하는 클래스는 final이 아니어야 하며, 오버라이드될 메소드도 final이어서는 안됩니다.
⦁ 추상 메소드를 가진 클래스를 단위 테스트하는 것은 해당 클래스를 직접 서브클래싱하고 추상 메소드에 대한 스텁 구현을 제공해야 합니다.
⦁ 구체적인 메소드는 컴포넌트 스캐닝에도 필요한데, 이는 구체적인 클래스를 선택하는 데 필요합니다.
⦁ 더욱 중요한 제한 사항은, 조회 메소드가 팩토리 메소드와 특히 설정 클래스 내의 @Bean 메소드와 작동하지 않는다는 것인데, 이 경우 컨테이너가 인스턴스 생성을 담당하지 않기 때문에, 즉석에서 런타임 생성된 서브클래스를 생성할 수 없기 때문입니다.
이전 코드 스니펫에서의 CommandManager 클래스의 경우, Spring 컨테이너는 createCommand() 메소드의 구현을 동적으로 오버라이드합니다. CommandManager 클래스는 재작업된 예제에서 보여주듯이 Spring 의존성이 없습니다.
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
메소드가 주입될 클라이언트 클래스(이 경우 CommandManager)에서, 주입될 메소드는 다음과 같은 형식의 시그니처를 요구합니다:
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
메서드가 추상적이면 동적으로 생성된 하위 클래스가 메서드를 구현합니다. 그렇지 않으면 동적으로 생성된 하위 클래스가 원래 클래스에 정의된 구체적 메서드를 오버라이드합니다. 다음 예를 고려하세요.
<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
<!-- inject dependencies here as required -->
</bean>
<!-- commandManager uses myCommand prototype bean -->
<bean id="commandManager" class="fiona.apple.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
</bean>
commandManager로 식별된 빈은 myCommand 빈의 새 인스턴스가 필요할 때마다 자체 createCommand() 메서드를 호출합니다. 실제로 필요한 경우 myCommand 빈을 프로토타입으로 배포하는 데 주의해야 합니다. 싱글톤인 경우 매번 동일한 myCommand 빈 인스턴스가 반환됩니다.
또는 다음 예제와 같이 어노테이션 기반 구성 메타데이터 모델 내에서 @Lookup 어노테이션을 통해 조회 메서드를 선언할 수 있습니다.
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
또는 보다 관용적으로, 조회 메서드의 선언된 리턴 타입에 대해 타겟 빈이 해결되는 것을 기대할 수 있습니다.
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup
protected abstract Command createCommand();
}
일반적으로 이러한 어노테이션이 달린 조회 메서드는 구체적인 스텁 구현으로 선언해야 하며, 이를 통해 추상 클래스가 기본적으로 무시되는 Spring의 구성 엘리먼트 스캐닝 규칙과 호환될 수 있습니다. 이 제한은 명시적으로 등록되거나 명시적으로 가져온 빈 클래스에는 적용되지 않습니다.
Scope가 다르게 지정된 타겟 빈에 액세스하는 또 다른 방법은 ObjectFactory/Provider 주입 지점입니다. 종속성으로서의 범위가 지정된 빈을 참조하세요.
ServiceLocatorFactoryBean(org.springframework.beans.factory.config 패키지에 있음)도 유용할 수 있습니다.
Arbitrary Method Replacement
조회 메서드 주입보다 덜 유용한 메서드 주입 형태는 관리되는 빈의 임의의 메서드를 다른 메서드 구현으로 대체하는 기능입니다. 이 기능이 실제로 필요할 때까지 이 섹션의 나머지 부분은 건너뛸 수 있습니다.
XML 기반 구성 메타데이터를 사용하면 배포된 빈에 대해 replace-method 엘리먼트를 사용하여 기존 메서드 구현을 다른 메서드 구현으로 대체할 수 있습니다. 다음 클래스를 고려해 보세요. 여기에는 오버라이드하려는 ComputeValue라는 메서드가 있습니다.
public class MyValueCalculator {
public String computeValue(String input) {
// some real code...
}
// some other methods...
}
org.springframework.beans.factory.support.MethodReplacer 인터페이스를 구현하는 클래스는 다음 예제와 같이 새로운 메서드 정의를 제공합니다.
/**
* meant to be used to override the existing computeValue(String)
* implementation in MyValueCalculator
*/
public class ReplacementComputeValue implements MethodReplacer {
public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
// get the input value, work with it, and return a computed result
String input = (String) args[0];
...
return ...;
}
}
Bean Scopes
빈 정의를 생성할 때, 해당 빈 정의에 의해 정의된 클래스의 실제 인스턴스를 생성하기 위한 레시피를 만듭니다. 빈 정의가 레시피라는 생각은 중요한데, 이는 클래스와 마찬가지로 하나의 레시피에서 많은 객체 인스턴스를 생성할 수 있다는 것을 의미합니다.
특정 빈 정의에서 생성된 객체에 주입될 다양한 의존성과 구성 값뿐만 아니라, 특정 빈 정의에서 생성된 객체의 범위를 제어할 수도 있습니다. 이 접근 방식은 강력하고 유연한데, 이는 자바 클래스 수준에서 객체의 범위를 구체화하는 대신 구성을 통해 생성하는 객체의 범위를 선택할 수 있기 때문입니다. 빈은 여러 범위 중 하나로 정의될 수 있습니다. Spring 프레임워크는 여섯 가지 범위를 지원하며, 이 중 네 가지는 web-aware ApplicationContext를 사용할 경우에만 사용할 수 있습니다. 또한 custom scope (사용자 정의 범위)생성할 수도 있습니다.
다음 표는 지원되는 범위를 설명합니다:
Table 1. Bean scopesScopeDescription
singleton | (Default) Scopes a single bean definition to a single object instance for each Spring IoC container. 각 Spring IoC 컨테이너마다 단일 빈 정의를 단일 객체 인스턴스로 범위 지정합니다. |
prototype | Scopes a single bean definition to any number of object instances. 단일 Bean 정의의 범위를 원하는 수의 객체 인스턴스로 지정합니다. |
request | Scopes a single bean definition to the lifecycle of a single HTTP request. That is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring ApplicationContext. 단일 HTTP 요청의 라이프사이클에 대해 단일 Bean 정의의 범위를 지정합니다. 즉, 각 HTTP 요청에는 단일 Bean 정의 뒤에 생성된 자체 Bean 인스턴스가 있습니다. 웹 인식 Spring ApplicationContext의 컨텍스트에서만 유효합니다. |
session | Scopes a single bean definition to the lifecycle of an HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext. 단일 Bean 정의의 범위를 HTTP 세션의 라이프사이클로 지정합니다. 웹 인식 Spring ApplicationContext의 컨텍스트에서만 유효합니다. |
application | Scopes a single bean definition to the lifecycle of a ServletContext. Only valid in the context of a web-aware Spring ApplicationContext. 단일 Bean 정의의 범위를 ServletContext의 라이프사이클로 지정합니다. 웹 인식 Spring ApplicationContext의 컨텍스트에서만 유효합니다. |
websocket | Scopes a single bean definition to the lifecycle of a WebSocket. Only valid in the context of a web-aware Spring ApplicationContext. 단일 Bean 정의의 범위를 WebSocket의 라이프사이클로 지정합니다. 웹 인식 Spring ApplicationContext의 컨텍스트에서만 유효합니다. |
스레드 범위를 사용할 수 있지만 기본적으로 등록되지 않습니다. 자세한 내용은 SimpleThreadScope 설명서를 참조하세요. 이 범위 또는 다른 사용자 정의 범위를 등록하는 방법에 대한 지침은 사용자 정의 범위 사용을 참조하세요.
The Singleton Scope
싱글톤 빈의 경우 단 하나의 공유 인스턴스만 관리되며, 해당 빈 정의와 일치하는 ID 또는 IDs를 가진 빈에 대한 모든 요청은 Spring 컨테이너에 의해 특정한 그 하나의 빈 인스턴스가 반환됩니다.
다른 방식으로 설명하자면, 빈 정의를 정의하고 그것을 싱글톤으로 범위 지정할 때, Spring IoC 컨테이너는 해당 빈 정의에 의해 정의된 객체의 정확히 하나의 인스턴스를 생성합니다. 이 단일 인스턴스는 싱글톤 빈의 캐시에 저장되며, 그 명명된 빈에 대한 모든 후속 요청 및 참조는 캐시된 객체를 반환합니다. 다음 이미지는 싱글톤 범위가 어떻게 작동하는지 보여줍니다:
Spring의 싱글톤 빈 개념은 GoF(Gang of Four) 패턴 책에 정의된 싱글톤 패턴과 다릅니다. GoF 싱글톤은 ClassLoader당 하나의 특정 클래스 인스턴스만 생성되도록 객체의 범위를 하드코딩합니다. Spring 싱글톤의 범위는 컨테이너별 및 Bean별 범위로 가장 잘 설명됩니다. 이는 단일 Spring 컨테이너의 특정 클래스에 대해 하나의 Bean을 정의하면 Spring 컨테이너는 해당 Bean 정의에 의해 정의된 클래스의 인스턴스를 하나만 생성한다는 의미입니다. 싱글톤 범위는 Spring의 기본 범위입니다. Bean을 XML의 싱글톤으로 정의하려면 다음 예제와 같이 Bean을 정의할 수 있습니다.
<bean id="accountService" class="com.something.DefaultAccountService"/>
<!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
The Prototype Scope
다음 다이어그램은 Spring 프로토타입 범위를 설명합니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class AppConfig {
@Bean
@Scope("prototype")
public DefaultAccountService accountService() {
return new DefaultAccountService();
}
}
다른 스코프와 달리, Spring은 프로토타입 빈의 전체 생명주기를 관리하지 않습니다. 컨테이너는 프로토타입 객체를 인스턴스화하고, 구성하며, 그 외에 조립한 후 클라이언트에게 전달하지만, 그 프로토타입 인스턴스의 추가적인 기록은 남기지 않습니다. 따라서, 초기화 생명주기 콜백 메소드가 범위에 관계없이 모든 객체에 대해 호출되지만, 프로토타입의 경우에는 구성된 소멸 생명주기 콜백이 호출되지 않습니다. 클라이언트 코드는 프로토타입 범위의 객체를 정리하고 프로토타입 빈이 보유한 고가의 자원을 해제해야 합니다. 프로토타입 범위의 빈이 보유한 자원을 Spring 컨테이너가 해제하도록 하려면, 정리가 필요한 빈에 대한 참조를 보유하는 bean post-processor 를 사용해 보세요.
어떤 면에서, Spring 컨테이너가 프로토타입 스코프 빈과 관련하여 수행하는 역할은 자바의 new 연산자를 대체하는 것입니다. 그 지점 이후의 모든 생명주기 관리는 클라이언트에 의해 처리되어야 합니다. (Spring 컨테이너에서 빈의 생명주기에 대한 자세한 내용은 Lifecycle Callbacks 을 참조하세요.)
스프링 빈 라이프사이클 콜백 메소드 예:
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
public class MyBean {
// 빈이 생성되고, 의존성 주입이 완료된 후 호출되는 메소드
@PostConstruct
public void init() {
// 초기화 로직
System.out.println("MyBean is initialized");
}
// 빈이 소멸되기 전에 호출되는 메소드
@PreDestroy
public void destroy() {
// 정리 로직
System.out.println("MyBean is destroyed");
}
}
@Configuration
public class AppConfig {
@Bean
public MyBean myBean() {
return new MyBean();
}
}
Singleton Beans with Prototype-bean Dependencies
싱글톤 스코프 빈이 프로토타입 빈에 의존하는 경우, 의존성은 인스턴스 생성 시에 해결된다는 점을 인지해야 합니다. 따라서 싱글톤 스코프 빈에 프로토타입 스코프 빈을 의존성 주입할 경우, 새로운 프로토타입 빈이 인스턴스화되고 그 후 싱글톤 빈에 의존성 주입됩니다. 이 프로토타입 인스턴스는 싱글톤 스코프 빈에 공급되는 유일한 인스턴스입니다.
하지만, 싱글톤 스코프 빈이 런타임에 반복적으로 프로토타입 스코프 빈의 새 인스턴스를 획득하길 원한다고 가정해 봅시다. 싱글톤 빈에 프로토타입 스코프 빈을 의존성 주입할 수는 없습니다. 왜냐하면 그 주입은 오직 한 번, 즉 Spring 컨테이너가 싱글톤 빈을 인스턴스화하고 그 의존성을 해결하고 주입할 때만 발생하기 때문입니다. 런타임에 프로토타입 빈의 새 인스턴스가 한 번 이상 필요한 경우, Method Injection 을 참조하십시오.
Customizing the Nature of a Bean
Lifecycle Callbacks
컨테이너의 빈 생명주기 관리와 상호 작용하려면, 스프링의 InitializingBean과 DisposableBean 인터페이스를 구현할 수 있습니다. 컨테이너는 전자에 대해서는 afterPropertiesSet()을, 후자에 대해서는 destroy()를 호출하여, 빈의 초기화와 소멸 시 특정 행동을 수행할 수 있게 합니다.
현재 Spring 애플리케이션에서 생명주기 콜백을 받기 위해 JSR-250의 @PostConstruct와 @PreDestroy 어노테이션을 사용하는 것이 일반적으로 최선의 방법으로 간주됩니다. 이 어노테이션들을 사용하면, 빈이 Spring 특정 인터페이스에 결합되지 않습니다. 자세한 내용은 @PostConstruct와 @PreDestroy 사용하기를 참조하세요. JSR-250 어노테이션을 사용하고 싶지 않지만 여전히 결합을 제거하고 싶다면, init-method와 destroy-method 빈 정의 메타데이터를 고려하세요.
JSR-250이란?
JSR-250은 Java Community Process(JCP)를 통해 개발된 Java 표준 기술의 일부입니다. JSR(Java Specification Request)은 Java 플랫폼에 추가하고자 하는 새로운 사양이나 기존 사양의 변경을 제안하는 공식 문서입니다. JSR-250은 "Common Annotations for the Java Platform"으로 명명되어 있으며, 자바 플랫폼 전반에 걸쳐 공통적으로 사용되는 어노테이션들을 정의합니다. JSR-250에 포함된 주요 어노테이션들 중 일부는 다음과 같습니다: - `@PostConstruct`: 객체가 생성되고 의존성 주입이 완료된 후 실행되어야 하는 메소드에 사용됩니다. 초기화 작업을 위해 종종 사용됩니다. - `@PreDestroy`: 객체가 소멸되기 전에 실행되어야 하는 메소드에 사용됩니다. 주로 정리 작업을 위해 사용됩니다. - `@Resource`: 의존성 주입을 위해 사용됩니다. 특정 자원에 대한 참조를 주입하는 데 사용됩니다. JSR-250은 Java EE 표준의 일부로 채택되었으며, 이후 Spring과 같은 프레임워크에서도 널리 사용되고 있습니다. 이 어노테이션들은 플랫폼에 종속되지 않는 방식으로 어플리케이션의 구성 요소를 정의하고 관리하는 데 도움을 줍니다.
내부적으로, Spring 프레임워크는 BeanPostProcessor 구현을 사용하여 찾을 수 있는 모든 콜백 인터페이스를 처리하고 적절한 메소드를 호출합니다. 기본적으로 Spring이 제공하지 않는 사용자 정의 기능이나 다른 생명주기 행동이 필요한 경우, 직접 BeanPostProcessor를 구현할 수 있습니다. 자세한 정보는 Container Extension Points를 참조하세요.
초기화 및 소멸 콜백 외에도, Spring 관리 객체는 Lifecycle 인터페이스를 구현하여 컨테이너의 자체 생명주기에 의해 주도되는 시작 및 종료 과정에 참여할 수 있습니다.
이 섹션에서는 생명주기 콜백 인터페이스에 대해 설명합니다.
Initialization Callbacks
void afterPropertiesSet() throws Exception;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public ExampleBean exampleInitBean() {
return new ExampleBean();
}
}
public class ExampleBean {
public void init() {
// do some initialization work
}
}
앞서 제시된 예시는 다음 예시(두 개의 목록으로 구성됨)와 거의 동일한 효과를 가집니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public AnotherExampleBean exampleInitBean() {
return new AnotherExampleBean();
}
}
public class AnotherExampleBean implements InitializingBean {
@Override
public void afterPropertiesSet() {
// do some initialization work
}
}
그러나 앞서 언급된 두 예시 중 첫 번째 예시는 코드를 Spring에 결합시키지 않습니다.
(이 문장의 의미는 첫 번째 예시가 Spring 프레임워크에 특정한 인터페이스나 클래스에 의존하지 않는다는 것입니다. 즉, 해당 코드는 Spring에 독립적으로 작성되었으며, 다른 환경이나 프레임워크로 이전하는 데에 있어서도 더 유연하게 대처할 수 있습니다.
이는 일반적으로 좋은 소프트웨어 설계 원칙을 따르는 것으로, 특정 프레임워크나 라이브러리에 강하게 결합(coupling)되는 것을 피하고, 대신 유지보수성과 확장성을 높이기 위해 느슨한 결합(loose coupling)과 모듈성을 추구하는 것을 의미합니다. 첫 번째 예시에서는 @PostConstruct, @PreDestroy 어노테이션 또는 POJO 초기화 메소드를 사용하여 Spring에 특화된 인터페이스(InitializingBean, DisposableBean)를 사용하지 않음으로써 이를 달성하고 있습니다)
@PostConstruct와 일반적인 초기화 메소드는 컨테이너의 싱글톤 생성 락에서 실행된다는 점을 인지하십시오. 빈 인스턴스는 @PostConstruct 메소드에서 리턴된 후에야 완전히 초기화되었고 다른 빈에게 공개될 준비가 되었다고 간주됩니다. 이러한 개별 초기화 메소드는 구성 상태를 검증하고 주어진 구성에 기반한 일부 데이터 구조를 준비하는 것을 목적으로 하지만, 외부 빈 접근과 관련된 추가 활동은 없어야 합니다. 그렇지 않으면 초기화 교착 상태(deadlock)의 위험이 있습니다. 비싼 비용이 드는 초기화 후 활동을 실행해야 하는 시나리오에서는, 예를 들어 비동기 데이터베이스 준비 단계와 같은 경우, 빈은 SmartInitializingSingleton.afterSingletonsInstantiated()를 구현하거나 컨텍스트 새로 고침 이벤트에 의존해야 합니다: ApplicationListener<ContextRefreshedEvent>를 구현하거나 해당 어노테이션 동등물 @EventListener(ContextRefreshedEvent.class)를 선언합니다. 이러한 변형들은 모든 일반 싱글톤 초기화 이후에 발생하므로 어떠한 싱글톤 생성 잠금 바깥에서 이루어집니다. 또한, (Smart)Lifecycle 인터페이스를 구현하고 컨테이너의 전반적인 생명주기 관리, 자동 시작 메커니즘, 소멸 전 정지 단계, 잠재적인 정지/재시작 콜백과 통합할 수도 있습니다(아래 참조).
Destruction Callbacks
org.springframework.beans.factory.DisposableBean 인터페이스를 구현하면, 빈을 포함하는 컨테이너가 Destroy 될 때 콜백을 받을 수 있습니다. DisposableBean 인터페이스는 단 하나의 메소드를 명시합니다:
void destroy() throws Exception;
DisposableBean 콜백 인터페이스의 사용은 권장하지 않습니다. 이는 코드를 Spring에 불필요하게 결합시키기 때문입니다. 대신, @PreDestroy 어노테이션을 사용하거나, 빈 정의에서 지원되는 일반적인 메소드를 지정하는 것이 좋습니다. XML 기반 구성 메타데이터에서는 <bean/> 태그에 destroy-method 속성을 사용할 수 있습니다. Java 구성에서는 @Bean의 destroyMethod 속성을 사용할 수 있습니다. 생명주기 콜백 수신을 참조하세요. 다음 정의를 고려해 보세요:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(destroyMethod = "cleanup")
public ExampleBean exampleDestructionBean() {
return new ExampleBean();
}
}
public class ExampleBean {
public void cleanup() {
// do some destruction work (like releasing pooled connections)
}
}
앞서 제시된 정의는 다음 정의와 거의 동일한 효과를 가집니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public AnotherExampleBean exampleDestructionBean() {
return new AnotherExampleBean();
}
}
public class AnotherExampleBean implements DisposableBean {
@Override
public void destroy() {
// do some destruction work (like releasing pooled connections)
}
}
그러나 두 가지 이전 정의 중 첫 번째 정의는 코드를 Spring과 결합시키지 않습니다.
또한 Spring은 destroy 메서드를 추론하는 기능을 지원하며, public close 또는 shutdown 메서드를 감지합니다. 이것은 Java 구성 클래스의 @Bean 메서드에 대한 기본 동작이며, 자동으로 java.lang.AutoCloseable 또는 java.io.Closeable 구현과 일치하며, 이로 인해 destory 로직이 Spring과 결합되지 않습니다.
XML을 사용하여 destroy 메서드 추론을 수행하려면 <bean> 요소의 destroy-method 속성에 특별한 (추론된) 값을 할당하면 됩니다. 이 값은 Spring에게 특정 빈 정의에 대해 빈 클래스에서 public close 또는 shutdown 메서드를 자동으로 감지하도록 지시합니다. 또한 <beans> 요소의 default-destroy-method 속성에 이 특별한 (추론된) 값을 설정하여 이 동작을 여러 빈 정의 집합에 적용할 수도 있습니다.
아래는 이를 자바 기반 구성 정보로 변환한 것입니다:
1. 특정 빈 정의에 대한 destroy 메서드 추론:
@Configuration
public class AppConfig {
@Bean(destroyMethod = "close")
public MyBean myBean() {
return new MyBean();
}
}
위의 코드에서 @Bean 어노테이션 내에서 destroyMethod 속성을 설정하여 빈이 destroy될 때 호출할 메서드를 지정할 수 있습니다. 위 예제에서는 close라는 이름의 메서드가 빈 destory 시 자동으로 호출됩니다.
2. 여러 빈 정의에 대한 default-destroy-method 설정:
@Configuration
public class AppConfig {
@Bean
public MyBean myBean1() {
return new MyBean();
}
@Bean
public MyBean myBean2() {
return new MyBean();
}
// 다른 빈들의 destroy 메서드를 설정하지 않고, 기본적으로 "close" 메서드를 사용하도록 설정
@Bean(destroyMethod = "")
public MyBean myBean3() {
return new MyBean();
}
}
위의 코드에서 @Bean(destroyMethod = "")을 사용하여 myBean3() 메서드에 대한 빈 정의에서 destory 메서드를 설정하지 않고, 대신 기본 값인 close 메서드를 사용하도록 설정합니다. 이것은 여러 빈 정의에 동일한 destroy 메서드를 적용하려는 경우에 유용합니다.
Default Initialization and Destroy Methods
Spring 컨테이너를 설정하여 모든 빈에 대한 init 및 destroy 콜백 메서드 이름을 "찾도록" 구성할 수 있습니다. 이것은 응용 프로그램 개발자로서 init()라는 초기화 콜백을 작성하고 각 빈 정의에 init-method="init" 속성을 구성할 필요 없이 사용할 수 있음을 의미합니다. Spring IoC 컨테이너는 해당 메서드를 빈이 생성될 때 호출하며(이전에 설명한 표준 라이프사이클 콜백 계약에 따라), 이 기능은 init 및 destroy 메서드 콜백에 대한 일관된 명명 규칙을 강제합니다.
만약 초기화 콜백 메서드가 init()로 명명되고 destroy 콜백 메서드가 destroy()로 명명되었다면, 아래 예제와 같이 클래스가 보일 것입니다:
public class DefaultBlogService implements BlogService {
private BlogDao blogDao;
public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
}
// this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
}
}
그런 다음 다음과 유사한 형태의 빈에서 해당 클래스를 사용할 수 있습니다:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public DefaultBlogService blogService() {
DefaultBlogService blogService = new DefaultBlogService();
blogService.setBlogDao(blogDao());
return blogService;
}
@Bean
public BlogDao blogDao() {
return new BlogDao();
}
}
'Spring Framework > Spring IoC' 카테고리의 다른 글
Spring IoC 컨테이너 이해하기: 제5편 (0) | 2024.08.13 |
---|---|
스프링 프레임워크의 의존성 주입 - 유연한 애플리케이션 설계의 핵심 (0) | 2024.08.08 |
Spring IoC 컨테이너 이해하기: 제3편 (0) | 2024.08.08 |
Spring IoC 컨테이너 이해하기: 제2편 (0) | 2024.08.08 |
Spring IoC 컨테이너 이해하기: 제1편 (0) | 2024.08.06 |