Dependencies and Configuration in Detail(한글)

이전 섹션에서 언급했듯이, Bean 프로퍼티와 생성자 인수를 다른 관리되는 Bean(협업자)에 대한 참조로 정의하거나 인라인으로 정의된 값으로 정의할 수 있습니다. Spring의 XML 기반 구성 메타데이터는 이러한 목적을 위해 <property/><constructor-arg/> 요소 내에서 하위 요소 유형을 지원합니다.

스트레이트 값(프리미티브, 문자열 등)(Straight Values (Primitives, Strings, and so on))

<property/> 요소의 value 속성은 속성 또는 생성자 인수를 사람이 읽을 수 있는 문자열 표현으로 지정합니다. 이러한 값을 '문자열’에서 속성 또는 인수의 실제 유형으로 변환하기 위해 Spring의 conversion service가 사용됩니다. 다음 예제에서는 다양한 값이 설정되는 것을 보여줍니다:

<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 구성을 위해 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이 더 간결합니다. 그러나 Bean 정의를 생성할 때 자동 속성 완성을 지원하는 IDE(예: IntelliJ IDEA 또는 Spring Tools for Eclipse)를 사용하지 않는 한 오타가 설계 시점이 아닌 런타임에 발견됩니다. 이러한 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>

Spring 컨테이너는 <value/> 요소 내부의 텍스트를 JavaBeans PropertyEditor 메커니즘을 사용하여 java.util.Properties 인스턴스로 변환합니다. 이것은 좋은 지름길이며, Spring 팀에서 value 속성 스타일보다 중첩된 <value/> 요소의 사용을 선호하는 몇 안 되는 곳 중 하나입니다.

idref 요소(The idref element)

idref 요소는 컨테이너에 있는 다른 Bean의 id(참조가 아닌 문자열 값)를 <constructor-arg/> 또는 <property/> 요소에 전달하는 오류 방지 방법입니다. 다음 예제는 이를 사용하는 방법을 보여줍니다:

<bean id="theTargetBean" class="..."/>

<bean id="theClientBean" class="...">
	<property name="targetName">
		<idref bean="theTargetBean"/>
	</property>
</bean>

앞의 Bean 정의 스니펫은 (런타임에) 정확히 다음과 같은 코드 조각과 정확히 동일합니다:

<bean id="theTargetBean" class="..." />

<bean id="client" class="...">
	<property name="targetName" value="theTargetBean"/>
</bean>

첫 번째 형태가 두 번째 형태보다 선호되는데, idref 태그를 사용하면 컨테이너가 배포 시점에 참조된 이름의 Bean이 실제로 존재하는지 확인할 수 있기 때문입니다. 두 번째 변형에서는 client Bean의 targetName 속성으로 전달되는 값에 대한 유효성 검사가 수행되지 않습니다. client Bean이 실제로 인스턴스화될 때만 오타가 발견됩니다(대부분 치명적인 결과를 초래할 수 있음). client Bean이 prototype Bean인 경우, 이 오타 및 그에 따른 예외는 컨테이너가 배포된 후 한참 후에야 발견될 수 있습니다.

참고: idref 요소의 local 속성은 더 이상 일반 bean 참조에 대한 값을 제공하지 않으므로 4.0 beans XSD에서 더 이상 지원되지 않습니다. 4.0 스키마로 업그레이드할 때는 기존 idref local 참조를 `idref bean`으로 변경하세요.

idref/>` 요소가 가치를 제공하는 일반적인 위치(적어도 Spring 2.0 이전 버전에서는)는 ProxyFactoryBean Bean 정의의 AOP 인터셉터의 구성입니다. 인터셉터 이름을 지정할 때 <idref/> 요소를 사용하면 인터셉터 ID의 철자를 잘못 입력하는 것을 방지할 수 있습니다.

다른 Bean에 대한 참조(협업자)(References to Other Beans (Collaborators))

ref 요소는 <constructor-arg/> 또는 <property/> 정의 요소 내부의 마지막 요소입니다. 여기에서는 Bean의 지정된 속성 값을 컨테이너가 관리하는 다른 Bean(협업자)에 대한 참조로 설정합니다. 참조된 Bean은 속성을 설정하려는 Bean의 종속성이며, 속성이 설정되기 전에 필요에 따라 초기화됩니다. (협업자가 싱글톤 Bean인 경우 컨테이너에 의해 이미 초기화될 수 있습니다.) 모든 참조는 궁극적으로 다른 객체에 대한 참조입니다. 범위 지정 및 유효성 검사는 bean 또는 parent 속성을 통해 다른 객체의 ID 또는 이름을 지정하는지 여부에 따라 달라집니다.

<ref/> 태그의 bean 속성을 통해 대상 Bean을 지정하는 것이 가장 일반적인 형태이며, 동일한 XML 파일에 있는지 여부에 관계없이 동일한 컨테이너 또는 상위 컨테이너에 있는 모든 Bean에 대한 참조를 생성할 수 있습니다. bean 속성의 값은 대상 Bean의 id 속성과 동일하거나 대상 Bean의 name 속성의 값 중 하나와 동일할 수 있습니다. 다음 예는 ref 요소를 사용하는 방법을 보여줍니다:

<ref bean="someBean"/>

parent 속성을 통해 대상 Bean을 지정하면 현재 컨테이너의 부모 컨테이너에 있는 Bean에 대한 참조가 생성됩니다. parent` 속성의 값은 대상 Bean의 id 속성 또는 대상 Bean의 name 속성에 있는 값 중 하나와 동일할 수 있습니다. 대상 Bean은 현재 Bean의 상위 컨테이너에 있어야 합니다. 이 Bean 참조 변형은 주로 컨테이너 계층 구조가 있고 기존 Bean을 부모 Bean과 이름이 같은 프록시를 사용하여 부모 컨테이너에 래핑하려는 경우에 사용해야 합니다. 다음 목록은 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>
ref 요소의 local 속성은 더 이상 일반 bean 참조에 대한 값을 제공하지 않으므로 4.0 bean XSD에서 더 이상 지원되지 않습니다. 4.0 스키마로 업그레이드할 때 기존 ref local 참조를 `ref bean`으로 변경하세요.

내부 Bean(Inner Beans)

다음 예제에서 볼 수 있듯이 <property/> 또는 <constructor-arg/> 요소 안의 <bean/> 요소는 내부 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>

내부 Bean 정의에는 정의된 ID나 이름이 필요하지 않습니다. 지정된 경우 컨테이너는 이러한 값을 식별자로 사용하지 않습니다. 또한 내부 Bean은 항상 익명이며 항상 외부 Bean과 함께 생성되므로 컨테이너는 생성 시 scope 플래그를 무시합니다. 내부 Bean에 독립적으로 접근하거나 둘러싸는 Bean이 아닌 다른 협업 Bean에 주입하는 것은 불가능합니다.

코너 케이스로서, 예를 들어 싱글톤 Bean 내에 포함된 요청 범위 내부 Bean에 대해 사용자 정의 범위에서 소멸 콜백을 수신할 수 있습니다. 내부 Bean 인스턴스의 생성은 포함된 Bean에 연결되지만, 파기 콜백을 통해 요청 범위의 라이프사이클에 참여할 수 있습니다. 이것은 일반적인 시나리오가 아닙니다. 내부 Bean은 일반적으로 단순히 포함 Bean의 범위를 공유합니다.

Collections

<`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>

맵 키 또는 값의 값 또는 집합 값은 다음 요소 중 하나라도 될 수 있습니다:,

bean | ref | idref | list | set | map | props | value | null

컬렉션 병합(Collection Merging)

Spring 컨테이너는 컬렉션 병합도 지원합니다. 애플리케이션 개발자는 부모 <list/>, <map/>, <set/> or <props/> element and have child <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.

다음 예는 컬렉션 병합을 보여줍니다:

<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>

child Bean 정의의 adminEmails 속성의 <props/> 요소에 merge=true 속성이 사용된 것을 주목하세요. child Bean이 컨테이너에 의해 확인되고 인스턴스화되면 결과 인스턴스에는 자식의 adminEmails 컬렉션과 상위의 adminEmails 컬렉션을 병합한 결과가 포함된 adminEmails Properties 컬렉션이 있습니다. 다음 목록은 결과를 보여줍니다:

administrator=administrator@example.com
sales=sales@example.com
support=support@example.co.uk

하위 Properties 컬렉션의 값 집합은 상위 <props/> 의 모든 속성 요소를 상속하며, 하위 support 값의 하위 값은 상위 컬렉션의 값을 재정의합니다.

이 병합 동작은 <list/>, <map/>, <set/> 컬렉션 타입에도 유사하게 적용됩니다. 특히 <list/> 요소의 경우, List 컬렉션 유형과 관련된 의미론(즉, 값의 순서가 지정된 컬렉션 개념)이 유지됩니다. 부모의 값은 모든 하위 목록의 값보다 우선합니다. Map, Set, Properties 컬렉션 타입의 경우 순서가 존재하지 않습니다. 따라서 컨테이너가 내부적으로 사용하는 관련 Map, Set, Properties 구현 타입의 기반이 되는 컬렉션 타입에는 순서 지정 시맨틱이 적용되지 않습니다.

컬렉션 병합의 제한 사항(Limitations of Collection Merging)

서로 다른 컬렉션 유형(예: MapList)은 병합할 수 없습니다. 병합을 시도하면 적절한 Exception`이 발생합니다. `merge 속성은 상속된 하위 자식 정의에 지정해야 합니다. 부모 컬렉션 정의에 merge 속성을 지정하는 것은 중복되며 원하는 병합이 이루어지지 않습니다.

강력한 타입 컬렉션(Strongly-typed collection)

Java의 제네릭 타입 지원 덕분에 강력한 타입의 컬렉션을 사용할 수 있습니다. 즉, (예를 들어) String 요소만 포함할 수 있도록 Collection 유형을 선언할 수 있습니다. Spring을 사용하여 강력하게 타입이 지정된 Collection 을 Bean에 의존적으로 주입하는 경우, 스프링의 타입 변환 지원을 활용하여 강력하게 타입이 지정된 Collection 인스턴스의 요소가 `Collection`에 추가되기 전에 적절한 타입으로 변환되도록 할 수 있습니다. 다음 Java 클래스와 Bean 정의는 이를 수행하는 방법을 보여줍니다:

  • Java

  • Kotlin

public class SomeClass {

	private Map<String, Float> accounts;

	public void setAccounts(Map<String, Float> accounts) {
		this.accounts = accounts;
	}
}
class SomeClass {
	lateinit var accounts: Map<String, Float>
}
<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>

something Bean의 accounts 속성을 주입할 준비가 되면, 강력하게 타입이 지정된 Map<String, Float> 의 요소 유형에 대한 제네릭 정보를 리플렉션을 통해 사용할 수 있습니다. 따라서 Spring의 타입 변환 인프라는 다양한 값 요소를 Float 타입으로 인식하고 문자열 값(9.99, 2.75, 3.99)은 실제 Float 타입으로 변환됩니다.

널 및 Bean 문자열 값(Null and Empty String Values)

Spring은 속성 등에 대한 Bean 인수를 Bean String 으로 취급합니다. 다음 XML 기반 구성 메타데이터 스니펫은 email 속성을 Bean String 값("")으로 설정합니다.

<bean class="ExampleBean">
	<property name="email" value=""/>
</bean>

앞의 예는 다음 Java 코드와 동일합니다:

  • Java

  • Kotlin

exampleBean.setEmail("");
exampleBean.email = ""

<null/> 요소는 null 값을 처리합니다. 다음 목록은 예시를 보여줍니다:

<bean class="ExampleBean">
	<property name="email">
		<null/>
	</property>
</bean>

앞의 구성은 다음 Java 코드와 동일합니다:

  • Java

  • Kotlin

exampleBean.setEmail(null);
exampleBean.email = null

p-namespace가 있는 XML 바로 가기(XML Shortcut with the p-namespace)

p-namespace를 사용하면 중첩된 <property/> 요소 대신 bean 요소의 속성을 사용하여 속성 값을 협업하는 Bean 또는 둘 다를 설명할 수 있습니다.

Spring은 XML 스키마 정의에 기반한 네임스페이스를 통해 확장 가능한 구성 형식을 지원합니다. 이 장에서 설명하는 beans 구성 형식은 XML 스키마 문서에 정의되어 있습니다. 그러나 p-namespace는 XSD 파일에 정의되어 있지 않으며 Spring의 코어에만 존재합니다.

다음 예제에서는 동일한 결과로 해석되는 두 개의 XML 스니펫(첫 번째는 표준 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 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>

이 예는 Bean 정의에서 email 이라는 p-namespace의 속성을 보여줍니다. 이것은 Spring에 속성 선언을 포함하도록 지시합니다. 앞서 언급했듯이 p-namespace에는 스키마 정의가 없으므로 속성 이름을 속성 이름으로 설정할 수 있습니다.

이 다음 예제에는 다른 Bean에 대한 참조가 있는 두 개의 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>

이 예제에는 p-namespace를 사용하는 속성 값뿐만 아니라 특수 형식을 사용하여 속성 참조를 선언하는 것도 포함되어 있습니다. 첫 번째 Bean 정의에서는 <property name="spouse" ref="jane"/> 을 사용하여 Bean john 에서 Bean jane 으로 참조를 생성하지만, 두 번째 Bean 정의에서는 p:spouse-ref="jane" 을 속성으로 사용하여 정확히 동일한 작업을 수행합니다. 이 경우 spouse 는 속성 이름이고, -ref 부분은 이것이 직선 값이 아니라 다른 Bean에 대한 참조임을 나타냅니다.

p-namespace는 표준 XML 형식만큼 유연하지 않습니다. 예를 들어, 속성 참조를 선언하는 형식은 `Ref`로 끝나는 속성과 충돌하지만 표준 XML 형식은 충돌하지 않습니다. 세 가지 방식을 동시에 사용하는 XML 문서를 만들지 않도록 접근 방식을 신중하게 선택하고 이를 팀원들에게 알리는 것이 좋습니다.

c-namespace가 있는 XML 바로 가기(XML Shortcut with the c-namespace)

스프링 3.1에 도입된 c 네임스페이스는 XML 바로 가기 with the p-namespace와 마찬가지로, 생성자 인수를 구성하기 위한 인라인 속성을 중첩된 constructor-arg 요소 대신 사용할 수 있도록 합니다.

다음 예제에서는 c: 네임스페이스를 사용하여 from 생성자 기반 의존성 주입과 동일한 작업을 수행합니다:

<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>

c: 네임스페이스는 생성자 인수를 이름으로 설정하기 위해 p: 하나(Bean 참조의 경우 후행 -ref)와 동일한 규칙을 사용합니다. 마찬가지로, XSD 스키마에 정의되어 있지 않더라도 XML 파일에 선언해야 합니다(Spring 코어 내부에 존재).

생성자 인자 이름을 사용할 수 없는 드문 경우(일반적으로 디버깅 정보 없이 바이트코드가 컴파일된 경우)에는 다음과 같이 인자 인덱스에 대한 폴백을 사용할 수 있습니다:

<!-- c-namespace index declaration -->
<bean id="beanOne" class="x.y.ThingOne" c:_0-ref="beanTwo" c:_1-ref="beanThree"
	c:_2="something@somewhere.com"/>
XML 문법으로 인해 인덱스 표기법에는 앞에 _ 가 있어야 하는데, 이는 XML 어트리뷰트 이름이 숫자로 시작할 수 없기 때문입니다(일부 IDE에서는 허용하지만). 해당 인덱스 표기법은 <constructor-arg> 요소에도 사용할 수 있지만 일반적으로 일반 선언 순서로 충분하기 때문에 일반적으로 사용되지는 않습니다.

실제로 생성자 확인 메커니즘은 인수를 일치시키는 데 매우 효율적이므로 꼭 필요한 경우가 아니라면 구성 전체에서 이름 표기를 사용하는 것이 좋습니다.

복합 프로퍼티 이름(Compound Property Names)

최종 속성 이름을 제외한 경로의 모든 구성 요소가 null 이 아니라면 Bean 속성을 설정할 때 복합 또는 중첩된 속성 이름을 사용할 수 있습니다. 다음 Bean 정의를 살펴보겠습니다:

<bean id="something" class="things.ThingOne">
	<property name="fred.bob.sammy" value="123" />
</bean>

something Bean에는 fred 속성이 있고, 그 속성에 bob 속성이 있으며, 그 속성에 sammy 속성이 있고, 최종 sammy 속성이 123 값으로 설정되고 있습니다. 이것이 작동하려면 Bean이 생성된 후 something`의 `fred 속성과 fred`의 `bob 속성이 null 이 아니어야 합니다. 그렇지 않으면 `NullPointerException`이 발생합니다.