Dependencies and Configuration in Detail
As mentioned in the previous section, you can define bean
properties and constructor arguments as references to other managed beans (collaborators)
or as values defined inline. Spring’s XML-based configuration metadata supports
sub-element types within its <property/>
and <constructor-arg/>
elements for this
purpose.
Straight Values (Primitives, Strings, and so on)
The value
attribute of the <property/>
element specifies a property or constructor
argument as a human-readable string representation. Spring’s
conversion service is used to convert these
values from a String
to the actual type of the property or argument.
The following example shows various values being set:
<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>
The following example uses the p-namespace for even more succinct XML configuration:
<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>
The preceding XML is more succinct. However, typos are discovered at runtime rather than design time, unless you use an IDE (such as IntelliJ IDEA or the Spring Tools for Eclipse) that supports automatic property completion when you create bean definitions. Such IDE assistance is highly recommended.
You can also configure a java.util.Properties
instance, as follows:
<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>
The Spring container converts the text inside the <value/>
element into a
java.util.Properties
instance by using the JavaBeans PropertyEditor
mechanism. This
is a nice shortcut, and is one of a few places where the Spring team do favor the use of
the nested <value/>
element over the value
attribute style.
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:
<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.
The local attribute on the idref 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 idref local references to idref bean when upgrading to the 4.0 schema.
|
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.
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.
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="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:
<!-- 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.
|
Inner Beans
A <bean/>
element inside the <property/>
or <constructor-arg/>
elements defines an
inner bean, as the following example shows:
<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.
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.
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:
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</prop>
<prop key="development">[email protected]</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:
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.
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:
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]</prop>
<prop key="support">[email protected]</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">[email protected]</prop>
<prop key="support">[email protected]</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:
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.
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.
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.
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
-
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>
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.
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 ("").
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
The preceding example is equivalent to the following Java code:
-
Java
-
Kotlin
exampleBean.setEmail("");
exampleBean.email = ""
The <null/>
element handles null
values. The following listing shows an example:
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
The preceding configuration is equivalent to the following Java code:
-
Java
-
Kotlin
exampleBean.setEmail(null);
exampleBean.email = 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:
<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="[email protected]"/>
</bean>
<bean name="p-namespace" class="com.example.ExampleBean"
p:email="[email protected]"/>
</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.
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.
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.
|
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:
<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="[email protected]"/>
</bean>
<!-- c-namespace declaration with argument names -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="[email protected]"/>
</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).
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="[email protected]"/>
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.
|
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:
<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.