This chapter covers the Spring Framework's implementation of the Inversion of Control (IoC) [1] principle
The Spring.Core assembly provides the basis for
the Spring.NET Inversion of Control container. The IObjectFactory
interface provides an advanced configuration mechanism capable of managing
objects of any nature. The IApplicationContext
interface builds on top of the IObjectFactory (it
is a sub-interface) and adds other functionality such as easier
integration with Spring.NET's Aspect Oriented Programming (AOP) features,
message resource handling (for use in internationalization), event
propagation and application layer-specific context such as
WebApplicationContext for use in web
applications.
In short, the IObjectFactory provides the
configuration framework and basic functionality, while the
IApplicationContext adds more enterprise-centric
functionality to it. The IApplicationContext is a
complete superset of the IObjectFactory, and any
description of IObjectFactory capabilities and
behavior should be considered to apply to
IApplicationContexts as well.
This chapter is divided into two parts, with the first part covering the basic principles
that apply to both the IObjectFactory and
IApplicationContext, with the second part covering those features
that apply only to the IApplicationContext
interface.
If you are new to Spring.NET or IoC containers in general, you may want to consider starting with Chapter 33, IoC Quickstarts, which contains a number of introductory level examples that actually demonstrate a lot of what is described in detail below. Don't worry if you don't absorb everything at once... those examples serve only to paint a picture of how Spring.NET hangs together in really broad brushstrokes. Once you have finished with those examples, you can come back to this section which will fill in all the fine detail.
The IObjectFactory is the actual
representation of the Spring IoC container that is responsible for
instantiating, configuring, and managing a number of objects.
The IObjectFactory interface is the central IoC
container interface in Spring. Its responsibilities include
instantiating or sourcing application objects, configuring such objects,
and assembling the dependencies between these objects.
There are a number of implementations of the
IObjectFactory interface that come supplied
straight out-of-the-box with Spring. The most commonly used
IObjectFactory implementation is the
XmlObjectFactory class. This implementation
allows you to express the objects that compose your application, and the
doubtless rich interdependencies between such objects, in terms of XML.
The XmlObjectFactory takes this XML configuration
metadata and uses it to create a fully configured system or application.
Interaction with the IObjectFactory interface is
discussed in Section 5.2.6, “Using the container”. Additional
features offered by another implementation of
IObjectFactory, the
IApplicationContext, are discussed in section
Section 5.10, “The IApplicationContext”.

As can be seen in the above image, the Spring IoC container consumes some form of configuration metadata; this configuration metadata is nothing more than how you (as an application developer) inform the Spring container as to how to “instantiate, configure, and assemble [the objects in your application]”. This configuration metadata is typically supplied in a simple and intuitive XML format. When using XML-based configuration metadata, you write object definitions for those object that you want the Spring IoC container to manage, and then let the container do it's stuff.
![]() | Note |
|---|---|
XML-based metadata is by far the most commonly used form of configuration metadata. It is not however the only form of configuration metadata that is allowed. The Spring IoC container itself is totally decoupled from the format in which this configuration metadata is actually written. Attribute based metadata will be part of an upcoming release and it is already part of the Spring Java framework. |
Spring configuration consists of at least one object definition that the container must manage, but typically there will be more than one object definition. When using XML-based configuration metadata, these object are configured as <object/> elements inside a top-level <objects/> element.
These object definitions correspond to the actual objects that make up your application. Typically you will have object definitions for your service layer objects, your data access objects (DAOs), presentation objects such as ASP.NET page instances, infrastructure objects such as NHibernate SessionFactories, and so forth. Typically one does not configure fine-grained domain objects in the container, because it is usually the responsibility of DAOs and business logic to create/load domain objects.
Find below an example of the basic structure of XML-based configuration metadata.
<objects xmlns="http://www.springframework.net">
<object id="..." type="...">
<!-- collaborators and configuration for this object go here -->
</object>
<object id="...." type="...">
<!-- collaborators and configuration for this object go here -->
</object>
<!-- more object definitions go here -->
</objects>Instantiating a Spring IoC container is straightforward.
IApplicationContext context = new XmlApplicationContext(
"file://services.xml",
"assembly://MyAssembly/MyDataAccess/data-access.xml");
// an IApplicationContext is also an IObjectFactory (via inheritance)
IObjectFactory factory = context;You can also create an container by using a custom configuration section in the standard .NET application (or web) configuration file. Once the container has been created you may never need to explicitly interact with it again in your code, for example when configuring ASP.NET pages.
You may be wondering what the assembly URL is all about. The above
example uses Spring.NET's IResource
abstraction. The IResource interface
provides a simple and uniform interface to a wide array of IO resources
that can represent themselves as
System.IO.Stream. An example for a file based
resource, not using the URL syntax but an implementation of the
IResource interface for file is shown below.
[C#]
IResource input = new FileSystemResource ("objects.xml");
IObjectFactory factory = new XmlObjectFactory(input);These resources are most frequently files or URLs but can also be
resources that have been embedded inside a .NET assembly. A simple URI
syntax is used to describe the location of the resource, which follows
the standard conventions for files, i.e.
file://object.xml and other well known protocols such
as http.
The following snippet shows the use of the URI syntax for
referring to a resource that has been embedded inside a .NET assembly,
assembly://<AssemblyName>/<NameSpace>/<ResourceName>
![]() | Note |
|---|---|
| To create an embedded resource using Visual Studio you must set the Build Action of the .xml configuration file to Embedded Resource in the file property editor. Also, you will need to explicitly rebuild the project containing the configuration file if it is the only change you make between successive builds. If using NAnt to build, add a <resources> section to the csc task. For example usage, look at the Spring.Core.Tests.build file included the distribution. |
The IResource abstraction is explained
further in Section 7.1, “Introduction”.
The preferred way to create an
IApplicationContext or
IObjectFactory is to use a custom configuration
section in the standard .NET application configuration file (one of
App.config or Web.config). A
custom configuration section that creates the same
IApplicationContext as the previous example is
<spring>
<context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
<resource uri="file://services.xml"/>
<resource uri="assembly://MyAssembly/MyDataAccess/data-access.xml"/>
</context>
</spring> The context type (specified as the value of
the type attribute of the context
element) is wholly optional, and defaults to the
Spring.Context.Support.XmlApplicationContext
class, so the following XML snippet is functionally equivalent to the
first.
<spring>
<context>
<resource uri="file://services.xml"/>
<resource uri="assembly://MyAssembly/MyDataAccess/data-access.xml"/>
</context>
</spring> To acquire a reference to an
IApplicationContext using a custom configuration
section, one simply uses the following code;
IApplicationContext ctx = ContextRegistry.GetContext();
The ContextRegistry is used to both instantiate
the application context and to perform service locator style access to
other objects. (See Section 5.15, “Service Locator access” for more
information). The glue that makes this possible is an implementation of
the Base Class Library (BCL) provided
IConfigurationSectionHandler interface, namely
the Spring.Context.Support.ContextHandler class.
The handler class needs to be registered in the
configSections section of the .NET configuration file
as shown below.
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
</sectionGroup>
</configSections> This declaration now enables the use
of a custom context section starting at the spring
root element.
In some usage scenarios, user code will not have to explicitly
instantiate an appropriate implementation of the
IObjectFactory interface, since Spring.NET code
will do it. For example, the ASP.NET web layer provides support code to
load a Spring.NET IApplicationContext
automatically as part of the normal startup process of an ASP.NET web
application. Similar support for WinForms applications is being
investigated.
While programmatic manipulation of
IObjectFactory instances will be described later, the
following sections will concentrate on describing the configuration of
objects managed by IObjectFactory instances.
Spring.NET comes with an XSD schema to make the validation of the
XML object definitions a whole lot easier. The XSD document is
thoroughly documented so feel free to take a peek inside (see Appendix C, Spring.NET's spring-objects.xsd). The XSD is currently used in the
implementation code to validate the XML document. The XSD schema serves
a dual purpose in that it also facilitates the editing of XML object
definitions inside an XSD aware editor (typically Visual Studio) by
providing validation (and Intellisense support in the case of Visual
Studio). You may wish to refer to Chapter 32, Visual Studio.NET Integration for more
information regarding such integration.
Your XML object definitions can also be defined within the
standard .NET application configuration file by registering the
Spring.Context.Support.DefaultSectionHandler
class as the configuration section handler for inline object
definitions. This allows you to completely configure one or more
IApplicationContext instances within a single
standard .NET application configuration file as shown in the following
example.
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects"/>
</context>
<objects xmlns="http://www.springframework.net">
...
</objects>
</spring>
</configuration>Other options available to structure the configuration files are described in Section 5.12.1, “Context Hierarchies” and ???.
The IApplicationContext can be configured
to register other resource handlers, custom parsers to integrate
user-contributed XML schema into the object definitions section, type
converters, and define type aliases. These features are discussed in
section Section 5.11, “Configuration of IApplicationContext”
It is often useful to split up container definitions into multiple XML files. One way to then load an application context which is configured from all these XML fragments is to use the application context constructor which takes multiple resource locations. With an object factory, an object definition reader can be used multiple times to read definitions from each file in turn.
Generally, the Spring.NET team prefers the above approach,
assembling individual files because it keeps container configuration
files unaware of the fact that they are being combined with others.
However, an alternate approach is to compose one XML object definition
file using one or more occurrences of the import
element to load definitions from other files. Any
import elements must be placed before
object elements in the file doing the importing.
Let's look at a sample:
<objects xmlns="http://www.springframework.net"> <import resource="services.xml"/> <import resource="resources/messageSource.xml"/> <import resource="/resources/themeSource.xml"/> <object id="object1" type="..."/> <object id="object2" type="..."/> </objects>
In this example, external object definitions are being loaded
from 3 files, services.xml,
messageSource.xml, and
themeSource.xml. All location paths are considered
relative to the definition file doing the importing, so
services.xml in this case must be in the same
directory as the file doing the importing, while
messageSource.xml and
themeSource.xml must be in a
resources location below the location of the
importing file. As you can see, a leading slash is actually ignored,
but given that these are considered relative paths, it is probably
better form not to use the slash at all. The contents of the files
being imported must be fully valid XML object definition files
according to the XSD, including the top level
objects element.
A Spring IoC container manages one or more objects. these objects are created using the configuration metadata that has been supplied to the container (typically in the form of XML <object/> definitions).
Within the container itself, these object definitions are represented as IObjectDefinition objects, which contain (among other information) the following metadata:
A type name: typically this is the actual implementation class of the object being defined..
Object behavioral configuration elements, which state how the object should behave in the Spring.NET IoC container (i.e. prototype or singleton, lifecycle callbacks, and so forth)
references to other objects which are needed for the object to do its work: these references are also called collaborators or dependencies.
other configuration settings to set in the newly created object. An example would be the number of threads to use in an object that manages a worker thread pool, or the size limit of the pool.
The concepts listed above directly translate to a set of elements the object definition consists of. These elements are listed below, along with a link to further documentation about each of them.
Table 5.1. Object definition explanation
| Feature | More info |
|---|---|
| type | Section 5.2.4, “Object creation” |
| id and name | ??? |
| singleton or prototype | ??? |
| object properties | Section 5.3.1, “Injecting dependencies” |
| constructor arguments | Section 5.3.1, “Injecting dependencies” |
| autowiring mode | Section 5.3.5, “Autowiring collaborators” |
| dependency checking mode | Section 5.3.6, “Checking for dependencies” |
| initialization method | Section 5.6.1, “Lifecycle interfaces” |
| destruction method | Section 5.6.1, “Lifecycle interfaces” |
Besides object definitions which contain information on how to
create a specific object, certain IObjectFactory
implementations also permit the registration of existing objects that
have been created outside the factory (by user code). The
DefaultListableObjectFactory class supports this
through the RegisterSingleton(..) method. (Typical
applications solely work with objects defined through metadata object
definitions though.)
Every object has one or more ids (also called
identifiers, or names; these terms refer to the same thing). These
ids must be unique within the container the object
is hosted in. An object will almost always have only one id, but if an
object has more than one id, the extra ones can essentially be
considered aliases.
When using XML-based configuration metadata, you use the
'id' or 'name'attributes to
specify the object identifier(s). The 'id'
attribute allows you to specify exactly one id, and as it is a real
XML element ID attribute, the XML parser is able to do some extra
validation when other elements reference the id; as such, it is the
preferred way to specify an object id. However, the XML specification
does limit the characters which are legal in XML IDs. This is usually
not a constraint, but if you have a need to use one of these special
XML characters, or want to introduce other aliases to the object, you
may also or instead specify one or more object ids,
separated by a comma (,), semicolon
(;), or whitespace in the 'name'
attribute.
Please note that you are not required to supply a name for a object. If no name is supplied explicitly, the container will generate a unique name for that object. The motivations for not supplying a name for a object will be discussed later (one use case is inner objects).
In an object definition itself, you may supply more than one name for the object, by using a combination of the id and name attributes as discussed in ???. This approach to aliasing objects has some limitations when you would like to assemble the main application configuration file from multiple files. See ??? for more information. This usage pattern is common when each configuration file represents a logical layer or component within the application. In this case you may want to refer to a common object dependency using a name that is specific to each file. If the common object dependency is defined in the main application configuration file itself, then one can use the name element as an alias mechanism. However, if the main application configuration file should not be responsible for defining the common object dependency, since it logically 'belongs' to one of the other layers or components, you can not use the name attribute to achieve this goal.
In this case, you can define an alias using an explicit
alias element contained in the main application
configuration file.
<alias name="fromName"
alias="toName"/>
This allows an object named fromName to be
referred to as toName across all application
configuration files.
As a concrete example, consider the case where the configuration file 'a.xml' (representing component A) defines a connection object called componentA-connection. In another file, 'b.xml' (representing component B) would like to refer to the connection as componentB-connection. And the main application, MyApp, defines its own XML fragment to assembles the final application configuration from all three fragments and would like to refer to the connection as myApp-connection. This scenario can be easily handled by adding to the MyApp XML fragment the following standalone aliases:
<alias
name="componentA-connection"
alias="componentB-connection"/>
<alias name="componentA-connection"
alias="myApp-connection"/>
Now each component and the main app can refer to the connection via a name that is unique and guaranteed not to clash with any other definition (effectively there is a namespace), yet they refer to the same object.
An object definition essentially is a recipe for creating one or more objects. The container looks at the recipe for a named object when asked, and uses the configuration metadata encapsulated by that object definition to create (or acquire) an actual object.
If you are using XML-based configuration metadata, you can specify
the type of object that is to be instantiated using the
'type' attribute of the
<object/> element. This
'type' attribute (which internally eventually boils
down to being a Type property on a
IObjectDefinition instance) is normally mandatory
(see XXX “Instantiation using an instance factory method” and XXX
“Object definition inheritance” for the two exceptions) and is used for
one of two purposes. The type property specifies the type of of the
object to be constructed in the common case where the container itself
directly creates the object by calling its constructor reflectively
(somewhat equivalent to C# code using the 'new'
operator). In the less common case where the container
invokes a static, factory method
on a class to create the object, the type property specifies the actual
class containing the static factory method that is to
be invoked to create the object (the type of the object returned from
the invocation of the static factory method may be
the same type or another type entirely, it doesn't matter).
When creating an object using the constructor approach, all normal classes are usable by and compatible with Spring. That is, the type being created does not need to implement any specific interfaces or be coded in a specific fashion. Just specifying the object type should be enough. However, depending on what type of IoC you are going to use for that specific object, you may need to create a default constructor (i.e. a constructor that has no parameters) in the source code definition of your class.
The XmlObjectFactory implementation of
the IObjectFactory interface can consume object
definitions that have been defined in XML, for example...
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/>
The mechanism for supplying arguments to the constructor (if required), or setting properties of the object instance after it has been constructed, is described shortly.
This XML fragment describes an object definition that will be
identified by the exampleObject name, instances
of which will be of the Examples.ExampleObject
type that has been compiled into the
ExamplesLibrary assembly. Take special note of the
structure of the type attribute's value... the
namespace-qualified name of the class is specified, followed by a
comma, followed by (at a bare minimum) the name of the assembly that
contains the class. In the preceding example, the
ExampleObject class is defined in the
Examples namespace, and it has been compiled into
the ExamplesLibrary assembly.
The name of the assembly that contains the type
must be specified in the type
attribute. Furthermore, it is recommended that you specify the fully
qualified assembly name [2] in order to guarantee that the type that Spring.NET uses
to instantiate your object (s) is indeed the one that you expect.
Usually this is only an issue if you are using classes from (strongly
named) assemblies that have been installed into the Global Assembly
Cache (GAC).
If you have defined nested classes use the addition symbol, +,
to reference the nested class. For example, if the class
Examples.ExampleObject had a nested class
Person the XML declaration would be
<object id="exampleObject" type="Examples.ExampleObject+Person, ExamplesLibrary"/>
If you are defining classes that have been compiled into
assemblies that are available to your application (such as the
bin directory in the case of ASP.NET applications)
via the standard assembly probing mechanisms, then you can specify
simply the name of the assembly (e.g.
ExamplesLibrary.Data)... this way, when (or if) the
assemblies used by your application are updated, you won't have to
change the value of every <object/>
definition's type attribute to reflect the new
version number (if the version number has changed)... Spring.NET will
automatically locate and use the newer versions of your assemblies
(and their attendant classes) from that point forward.
When defining an object which is to be created using a static factory method, along with the type attribute which specifies the type containing the static factory method, another attribute named factory-method is needed to specify the name of the factory method itself. Spring.NET expects to be able to call this method (with an optional list of arguments as described later) and get back a live object, which from that point on is treated as if it had been created normally via a constructor. One use for such an object definition is to call static factories in legacy code.
Following is an example of an object definition which specifies
that the object is to be created by calling a factory-method. Note
that the definition does not specify the type (class) of the returned
object, only the type containing the factory method. In this example,
CreateInstance must be a static method.
<object id="exampleObject"
type="Examples.ExampleObjectFactory, ExamplesLibrary"
factory-method="CreateInstance"/>The mechanism for supplying (optional) arguments to the factory method, or setting properties of the object instance after it has been returned from the factory, will be described shortly.
In a fashion similar to instantiation using a static factory
method, instantiation using an instance factory method is where a
non-static method of an existing object from the container is invoked
to create the new object. To use this mechanism, the
'type' attribute must be left empty, and the
'factory-object' attribute must specify the name of
an object in the current (or parent/ancestor) container that contains
the instance method that is to be invoked to create the object. The
name of the factory method itself should still be set via the
'factory-method' attribute.
<!-- the factory object, which contains an instance method called 'CreateInstance' -->
<object id="exampleFactory" type="...">
<!-- inject any dependencies required by this object -->
</object>
<!-- the object that is to be created by the factory object -->
<object id="exampleObject"
factory-method="CreateInstance"
factory-object="exampleFactory"/>Although the mechanisms for setting object properties are still to be discussed, one implication of this approach is that the factory object itself can be managed and configured via Dependency Injection, by the container.
![]() | Note |
|---|---|
When the Spring documentation makes mention of a 'factory
object', this will be a reference to an object that is configured
in the Spring container that will create objects via an instance
or static factory method. When the documentation mentions a
|
Generic types can also be created in much the same manner an non-generic types.
The following examples shows the definition of simple generic types and how they can be created in Spring's XML based configuration file.
namespace GenericsPlay
{
public class FilterableList<T>
{
private List<T> list;
private String name;
public List<T> Contents
{
get { return list; }
set { list = value; }
}
public String Name
{
get { return name; }
set { name = value; }
}
public List<T> ApplyFilter(string filterExpression)
{
/// should really apply filter to list ;)
return new List<T>();
}
}
}The XML configuration to create and configure this object is shown below
<object id="myFilteredIntList" type="GenericsPlay.FilterableList<int>, GenericsPlay"> <property name="Name" value="My Integer List"/> </object>
There are a few items to note in terms how to
specify a generic type. First, the left bracket that specifies the
generic type, i.e. <, is replaced with the
string < due to XML escape syntax for the less than symbol.
Yes, we all realize this is less than ideal from the readability point
of view. Second, the generic type arguments can not be fully assembly
qualified as the comma is used to separate generic type arguments.
Alternative characters used to overcome the two quirks can be
implemented in the future but so far, all proposals don't seem to help
clarify the text. The suggested solution to improve readability is to
use type aliases as shown below
<typeAliases> <alias name="GenericDictionary" type=" System.Collections.Generic.Dictionary<,>" /> <alias name="myDictionary" type="System.Collections.Generic.Dictionary<int,string>" /> </typeAliases>
So that instead of something like this
<object id="myGenericObject"
type="GenericsPlay.ExampleGenericObject<System.Collections.Generic.Dictionary<int , string>>, GenericsPlay" />It can be shortened to
<object id="myOtherGenericObject"
type="GenericsPlay.ExampleGenericObject<GenericDictionary<int , string>>, GenericsPlay" />or even shorter
<object id="myOtherOtherGenericObject"
type="GenericsPlay.ExampleGenericObject<MyIntStringDictionary>, GenericsPlay" />
Refer to Section 5.11, “Configuration of IApplicationContext” for additional information on using type aliases.
The following classes are used to demonstrate the ability to create instances of generic types that themselves are created via a static generic factory method.
public class TestGenericObject<T, U>
{
public TestGenericObject()
{
}
private IList<T> someGenericList = new List<T>();
private IDictionary<string, U> someStringKeyedDictionary =
new Dictionary<string, U>();
public IList<T> SomeGenericList
{
get { return someGenericList; }
set { someGenericList = value; }
}
public IDictionary<string, U> SomeStringKeyedDictionary
{
get { return someStringKeyedDictionary; }
set { someStringKeyedDictionary = value; }
}
}The accompanying factory class is
public class TestGenericObjectFactory
{
public static TestGenericObject<V, W> StaticCreateInstance<V, W>()
{
return new TestGenericObject<V, W>();
}
public TestGenericObject<V, W> CreateInstance<V, W>()
{
return new TestGenericObject<V, W>();
}
}
The XML snippet to create an instance of TestGenericObject
where V is a List of integers and
W is an integer is shown below
<object id="myTestGenericObject"
type="GenericsPlay.TestGenericObjectFactory, GenericsPlay"
factory-method="StaticCreateInstance<System.Collections.Generic.List<int>,int>"
/>The StaticCreateInstance method is responsible for instantiating the object that will be associated with the id 'myTestGenericObject'.
Using the class from the previous example the XML snippet to create an instance of a generic type via an instance factory method is shown below
<object id="exampleFactory" type="GenericsPlay.TestGenericObject<int,string>, GenericsPlay"/>
<object id="anotherTestGenericObject"
factory-object="exampleFactory"
factory-method="CreateInstance<System.Collections.Generic.List<int>,int>"/>
This creates an instance of
TestGenericObject<List<int>,int>
An IApplicationContext is essentially
nothing more than the interface for an advanced factory capable of
maintaining a registry of different objects and their dependencies. The
IApplicationContext enables you to read object
definitions and access them. You create one and read in some object
definition in the XML format as follows:
IApplicationContext context = new XmlApplicationContext("file://objects.xml");
Basically that is all there is to it. Using
GetObject(string) or the indexer
[string], you can retrieve instances of your object;
the client-side view of the IApplicationContext
is simple. The IApplicationContext interface has
just a few other methods related to finding objects in the contianer,
but ideally your application code should never use them... indeed, your
application code should have no calls to the
GetObject(string) method at all, and thus no
dependency on Spring APIs at all.
Your typical enterprise application is not made up of a single object. Even the simplest of applications will no doubt have at least a handful of objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of object definitions that stand-alone, each to themselves, to a fully realized application where objects work (or collaborate) together to achieve some goal (usually an application that does what the end-user wants).
The basic principle behind Dependency Injection (DI) is that objects define their dependencies (that is to say the other objects they work with) only through constructor arguments, arguments to a factory method, or properties which are set on the object instance after it has been constructed or returned from a factory method. Then, it is the job of the container to actually inject those dependencies when it creates the object. This is fundamentally the inverse, hence the name Inversion of Control (IoC), of the object itself being in control of instantiating or locating its dependencies on its own using direct construction of classes, or something like the Service Locator pattern.
It becomes evident upon usage that code gets much cleaner when the DI principle is applied, and reaching a higher grade of decoupling is much easier when objects do not look up their dependencies, but are provided with them (and additionally do not even know where the dependencies are located and of what concrete class they are). DI exists in two major variants, namely Constructor Injection and Setter Injection.
Constructor-based DI is effected by invoking a constructor with a number of arguments, each representing a dependency. Additionally, calling a static factory method with specific arguments to construct the object, can be considered almost equivalent, and the rest of this text will consider arguments to a constructor and arguments to a static factory method similarly. Find below an example of a class that could only be dependency injected using constructor injection. Notice that there is nothing special about this class.
public class SimpleMovieLister
{
// the SimpleMovieLister has a dependency on a MovieFinder
private IMovieFinder movieFinder;
// a constructor so that the Spring container can 'inject' a MovieFinder
public MovieLister(IMovieFinder movieFinder)
{
this.movieFinder = movieFinder;
}
// business logic that actually 'uses' the injected IMovieFinder is omitted...
}Constructor argument resolution matching occurs using the argument's type. If there is no potential for ambiguity in the constructor arguments of a object definition, then the order in which the constructor arguments are defined in a object definition is the order in which those arguments will be supplied to the appropriate constructor when it is being instantiated. Consider the following class:
namespace X.Y
{
public class Foo
{
public Foo(Bar bar, Baz baz)
{
// ...
}
}
}There is no potential for ambiguity here (assuming of course that Bar and Baz classes are not related in an inheritance hierarchy). Thus the following configuration will work just fine, and you do not need to specify the constructor argument indexes and / or types explicitly.
<object name="Foo" type="X.Y.Foo, Example">
<constructor-arg>
<object type="X.Y.Bar, Example"/>
</constructor-arg>
<constructor-arg>
<object type="X.Y.Baz, Example"/>
</constructor-arg>
</object>When another object is referenced, the type is known, and
matching can occur (as was the case with the preceding example).
When a simple type is used, such as
<value>true<value>, Spring cannot
determine the type of the value, and so cannot match by type without
help. Consider the following class:
using System;
namespace SimpleApp
{
public class ExampleObject
{
private int years; //No. of years to the calculate the Ultimate Answer
private string ultimateAnswer; //The Answer to Life, the Universe, and Everything
public ExampleObject(int years, string ultimateAnswer)
{
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}The above scenario can use type
matching with simple types by explicitly specifying the type of
the constructor argument using the type
attribute. For example:
<object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg type="int" value="7500000"/> <constructor-arg type="string" value="42"/> </object>
The type attribute specifies the
System.Type of the constructor argument, such
as System.Int32. Alias' are available to for common simple types
(and their array equivalents). These alias' are...
Table 5.2. Type aliases
| Type | Alias' | Array Alias' |
|---|---|---|
| System.Char | char, Char | char[], Char() |
| System.Int16 | short, Short | short[], Short() |
| System.Int32 | int, Integer | int[], Integer() |
| System.Int64 | long, Long | long[], Long() |
| System.UInt16 | ushort | ushort[] |
| System.UInt32 | uint | uint[] |
| System.UInt64 | ulong | ulong[] |
| System.Float | float, Single | float[], Single() |
| System.Double | double, Double | double[], Double() |
| System.Date | date, Date | date[], Date() |
| System.Decimal | decimal, Decimal | decimal[], Decimal() |
| System.Bool | bool, Boolean | bool[], Boolean() |
| System.String | string, String | string[], String() |
Constructor arguments can have their index specified
explicitly by use of the index attribute. For
example:
<object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </object>
As well as solving the ambiguity problem of multiple simple values, specifying an index also solves the problem of ambiguity where a constructor may have two arguments of the same type. Note that the index is 0 based.
Constructor arguments can also be specified by name by using
the name attribute of the
<constructor-arg> element.
<object name="exampleObject" type="SimpleApp.ExampleObject, SimpleApp"> <constructor-arg name="years" value="7500000"/> <constructor-arg name="ultimateAnswer" value="42"/> </object>
Setter-based DI is realized by calling setter methods on your objects after invoking a no-argument constructor or no-argument static factory method to instantiate your object.
Find below an example of a class that can only be dependency injected using pure setter injection.
public class MovieLister
{
private IMovieFinder movieFinder;
public IMovieFinder MovieFinder
{
set
{
movieFinder = value;
}
}
// business logic that actually 'uses' the injected IMovieFinder is omitted...
}The IObjectFactory supports both of these
variants for injecting dependencies into objects it manages. (It in
fact also supports injecting setter-based dependencies after some
dependencies have already been supplied via the constructor approach.)
The configuration for the dependencies comes in the form of the
IObjectDefinition class, which is used together
with TypeConverters to know how to convert
properties from one format to another. However, most users of
Spring.NET will not be dealing with these classes directly (that is
programatically), but rather with an XML definition file which will be
converted internally into instances of these classes, and used to load
an entire Spring IoC container instance.
Object dependency resolution generally happens as follows:
The IObjectFactory is created and
initialized with a configuration which describes all the
objects. Most Spring.NET users use an
IObjectFactory or
IApplicationContext variant that supports
XML format configuration files.
Each object has dependencies expressed in the form of properties, constructor arguments, or arguments to the static-factory method when that is used instead of a normal constructor. These dependencies will be provided to the object, when the object is actually created.
Each property or constructor argument is either an actual definition of the value to set, or a reference to another object in the container.
Each property
or constructor argument which is a value must be able to be
converted from whatever format it was specified in, to the
actual System.Type of that property or
constructor argument. By default Spring.NET can convert a value
supplied in string format to all built-in types, such as
int, long,
string, bool, etc.
Spring.NET uses TypeConverter definitions
to be able to convert string values to other, arbitrary types.
Refer to Section 6.3, “Type conversion” for more
information regarding type conversion, and how you can design
your classes to be convertible by Spring.NET.
The Spring container validates the configuration of each object
as the container is created, including the validation that properties
which are object references are actually referring to valid object.
However, the object properties themselves are not set until the object
is actually created. For those object that defined as singletons and
set to be pre-instantiated (such as singleton object in an
IApplicationContext), creation happens at the
time that the container is created, but otherwise this is only when
the object is requested. When an object actually has to be created,
this will potentially cause a graph of other objects to be created, as
its dependencies and its dependencies' dependencies (and so on) are
created and assigned.
You can generally trust Spring.NET to do the right thing. It
will detect configuration issues, such as references to non-existent
object definitions and circular dependencies, at container load-time.
It will actually set properties and resolve dependencies as late as
possible, which is when the object is actually created. This means
that a Spring container which has loaded correctly can later generate
an exception when you request an object if there is a problem creating
that object or one of its dependencies. This could happen if the
object throws an exception as a result of a missing or invalid
property, for example. This potentially delayed visibility of some
configuration issues is why IApplicationContext
by default pre-instantiates singleton objects. At the cost of some
upfront time and memory to create these objects before they are
actually needed, you find out about configuration issues when the
IApplicationContext is created, not later. If
you wish, you can still override this default behavior and set any of
these singleton objects to lazy-load (not be preinstantiated)
If no circular dependencies are involved (see sidebar for a
discussion of circular dependencies), when one or more collaborating
objects are being injected into a dependent object, each collaborating
object is totally configured prior to being passed (via one of the DI
flavors) to the dependent object. This means that if object A has a
dependency on object B, the Spring IoC container will
totally configure object B prior to invoking the
setter method on object A; you can read 'totally
configure' to mean that the object will be instantiated (if
not a pre-instantiated singleton), all of its dependencies will be
set, and the relevant lifecycle methods (such as a configured init
method or the IIntializingObject callback
method) will all be invoked.
First, an example of using XML-based configuration metadata for setter-based DI. Find below a smallpart of a Spring XML configuration file specifying some object definitions.
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary">
<!-- setter injection using the ref attribute -->
<property name="objectOne" ref="anotherExampleObject"/>
<property name="objectTwo" ref="yetAnotherObject"/>
<property name="IntegerProperty" value="1"/>
</object>
<object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/>
<object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/>
[C#]
public class ExampleObject
{
private AnotherObject objectOne;
private YetAnotherObject objectTwo;
private int i;
public AnotherObject ObjectOne
{
set { this.objectOne = value; }
}
public YetAnotherObject ObjectTwo
{
set { this.objectTwo = value; }
}
public int IntegerProperty
{
set { this.i = value; }
}
}As you can see, setters have been declared to match against the properties specified in the XML file. Find below an example of using constructor-based DI.
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary">
<constructor-arg name="objectOne" ref="anotherExampleObject"/>
<constructor-arg name="objectTwo" ref="yetAnotherObject"/>
<constructor-arg name="IntegerProperty" value="1"/>
</object>
<object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/>
<object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/>
[Visual Basic.NET]
Public Class ExampleObject
Private myObjectOne As AnotherObject
Private myObjectTwo As YetAnotherObject
Private i As Integer
Public Sub New (
anotherObject as AnotherObject,
yetAnotherObject as YetAnotherObject,
i as Integer)
myObjectOne = anotherObject
myObjectTwo = yetAnotherObject
Me.i = i
End Sub
End ClassAs you can see, the constructor arguments specified
in the object definition will be used to pass in as arguments to the
constructor of the ExampleObject.
Now consider a variant of this where instead of using a constructor, Spring is told to call a static factory method to return an instance of the object
<object id="exampleObject" type="Examples.ExampleFactoryMethodObject, ExamplesLibrary"
factory-method="CreateInstance">
<constructor-arg name="objectOne" ref="anotherExampleObject"/>
<constructor-arg name="objectTwo" ref="yetAnotherObject"/>
<constructor-arg name="intProp" value="1"/>
</object>
<object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/>
<object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/>
[C#]
public class ExampleFactoryMethodObject
{
private AnotherObject objectOne;
private YetAnotherObject objectTwo;
private int i;
// a private constructor
private ExampleFactoryMethodObject()
{
}
public static ExampleFactoryMethodObject CreateInstance(AnotherObject objectOne,
YetAnotherObject objectTwo,
int intProp)
{
ExampleFactoryMethodObject fmo = new ExampleFactoryMethodObject();
fmo.AnotherObject = objectOne;
fmo.YetAnotherObject = objectTwo;
fmo.IntegerProperty = intProp;
return fmo;
}
// Property definitions
}Note that arguments to the static factory method are supplied via constructor-arg elements, exactly the same as if a constructor had actually been used. These arguments are optional. Also, it is important to realize that the type of the class being returned by the factory method does not have to be of the same type as the class which contains the static factory method, although in this example it is. An instance (non-static) factory method, mentioned previously, would be used in an essentially identical fashion (aside from the use of the factory-object attribute instead of the type attribute), so will not be detailed here.
Note that Setter Injection and Constructor Injectionare not mutually exclusive. It is perfectly reasonable to use both for a single object definition, as can be seen in the following example:
<object id="exampleObject" type="Examples.MixedIocObject, ExamplesLibrary">
<constructor-arg name="objectOne" ref="anotherExampleObject"/>
<property name="objectTwo" ref="yetAnotherObject"/>
<property name="IntegerProperty" value="1"/>
</object>
<object id="anotherExampleObject" type="Examples.AnotherObject, ExamplesLibrary"/>
<object id="yetAnotherObject" type="Examples.YetAnotherObject, ExamplesLibrary"/>
[C#]
public class MixedIocObject
{
private AnotherObject objectOne;
private YetAnotherObject objectTwo;
private int i;
public MixedIocObject (AnotherObject obj)
{
this.objectOne = obj;
}
public YetAnotherObject ObjectTwo
{
set { this.objectTwo = value; }
}
public int IntegerProperty
{
set { this.i = value; }
}
}As mentioned in the previous section, object properties and
constructor arguments can be defined as either references to other
managed objects (collaborators), or values defined inline. Spring's
XML-based configuration metadata supports a number of sub-element types
within its <property/> and
<constructor-arg/> elements for this
purpose.a
The <value/> element specifies a
property or constructor argument as a human-readable string
representation. As mentioned previously,
TypeConverter instances are used to convert
these string values from a System.String to the
actual property or argument type. Custom
TypeConverter implementations in the
Spring.Objects.TypeConverters namespace are used to
augment the functionality offered by the .NET BCL's default
TypeConverter implementations.
In the following example, we use a
SqlConnection from the
System.Data.SqlClient namespace of the BCL. This
class (like many other existing classes) can easily be used in a
Spring.NET object factory, as it offers a convenient public property
for configuration of its ConnectionString
property.
objects xmlns="http://www.springframework.net">
<object id="myConnection" type="System.Data.SqlClient.SqlConnection">
<!-- results in a call to the setter of the ConnectionString property -->
<property
name="ConnectionString"
value="Integrated Security=SSPI;database=northwind;server=mySQLServer"/>
</object>
</objects>
An idref element is simply a shorthand and error-proof way to set a property to the String id or name of another object in the container.
<object id="theTargetObject" type="...">
. . .
</object>
<object id="theClientObject" type="...">
<property name="targetName">
<idref object="theTargetObject"/>
</property>
</object>This is exactly equivalent at runtime to the following fragment:
<object id="theTargetObject" type="..."> . . . </object> <object id="theClientObject" type="..."> <property name="targetName" value="theTargetObject"/> </object>
The main reason the first form is preferable
to the second is that using the idref tag will
allow Spring.NET to validate at deployment time that the other
object actually exists. In the second variation, the class that is
having its targetName property injected is
forced to do its own validation, and that will only happen when that
class is actually instantiated by the container, possibly long after
the container is actually up and running.
Additionally, if the object being referred to is in the same
actual XML file, and the object name is the object
id, the local attribute may
be used, which will allow the XML parser itself to validate the
object name even earlier, at parse time.
<property name="targetName"> <idref local="theTargetObject"/> </property>
The ref element is the final element allowed
inside a property definition element. It is used to
set the value of the specified property to be a reference to another
object managed by the container, a collaborator, so to speak. As you
saw in the previous example to set collection properties, we used the
SqlConnection instance from the initial example
as a collaborator and specified it using a <ref object/>
element. As mentioned in a previous section, the referred-to object is
considered to be a dependency of the object who's property is being
set, and will be initialized on demand as needed (if it is a singleton
object it may have already been initialized by the container) before
the property is set. All references are ultimately just a reference to
another object, but there are 3 variations on how the id/name of the
other object may be specified, which determines how scoping and
validation is handled.
Specifying the target object by using the
object attribute of the ref tag
is the most general form, and will allow creating a reference to any
object in the same IObjectFactory /
IApplicationContext (whether or not in the same
XML file), or parent IObjectFactory /
IApplicationContext. The value of the
object attribute may be the same as either the
id attribute of the target object, or one of the
values in the name attribute of the target
object.
<ref object="someObject"/>
Specifying the target object by using the
local attribute leverages the ability of the XML
parser to validate XML id references within the same file. The value
of the local attribute must be the same as the
id attribute of the target object. The XML parser
will issue an error if no matching element is found in the same file.
As such, using the local variant is the best choice (in order to know
about errors are early as possible) if the target object is in the
same XML file.
<ref local="someObject"/>
Specifying the target object by using the
parent attribute allows a reference to be created
to an object that is in a parent IObjectFactory
(orIApplicationContext) of the current
IObjectFactory (or
IApplicationContext). The value of the
parent attribute may be the same as either the
id attribute of the target object, or one of the
values in the name attribute of the target object,
and the target object must be in a
parent IObjectFactory or
IApplicationContext of the current one. The
main use of this object reference variant is when there is a need to
wrap an existing object in a parent context with some sort of proxy
(which may have the same name as the parent), and needs the original
object so it may wrap it.
<ref parent="someObject"/>
An object element inside the
property element is used to define an object value
inline, instead of referring to an object defined elsewhere in the
container. The inline object definition does not need to have any id
or name defined (indeed, if any are defined, they will be ignored).
<object id="outer" type="...">
<!-- Instead of using a reference to target, just use an inner object -->
<property name="target">
<object type="ExampleApp.Person, ExampleApp">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</object>
</property>
</object>The list, set,
name-values and dictionary
elements allow properties and arguments of the type
IList, ISet,
NameValueCollection and
IDictionary, respectively, to be defined and set.
<objects xmlns="http://www.springframework.net">
<object id="moreComplexObject" type="Example.ComplexObject">
<!--
results in a call to the setter of the SomeList (System.Collections.IList) property
-->
<property name="SomeList">
<list>
<value>a list element followed by a reference</value>
<ref object="myConnection"/>
</list>
</property>
<!--
results in a call to the setter of the SomeDictionary (System.Collections.IDictionary) property
-->
<property name="SomeDictionary">
<dictionary>
<entry key="a string => string entry" value="just some string"/>
<entry key-ref="myKeyObject" value-ref="myConnection"/>
</dictionary>
</property>
<!--
results in a call to the setter of the SomeNameValue (System.Collections.NameValueCollection) property
-->
<property name="SomeNameValue">
<name-values>
<add key="HarryPotter" value="The magic property"/>
<add key="JerrySeinfeld" value="The funny (to Americans) property"/>
</name-values>
</property>
<!--
results in a call to the setter of the SomeSet (Spring.Collections.ISet) property
-->
<property name="someSet">
<set>
<value>just some string</value>
<ref object="myConnection"/>
</set>
</property>
</object>
</objects>Many classes in the BCL expose only read-only properties for collection classes. When Spring.NET encounters a read-only collection, it will configure the collection by using the getter property to obtain a reference to the collection class and then proceed to add the additional elements to the existing collection. This results in an additive behavior for collection properties that are exposed in this manner.
Note that the value of a Dictionary entry, or a set value, can also again be any of the elements:
(object | ref | idref | expression | list | set | dictionary |
name-values | value | null)
The shortcut forms for value and references are useful to reduce XML verbosity when setting collection properties. See Section 5.3.2.8, “Value and ref shortcut forms” for more information.
Spring supports setting values for classes that expose
properties based on the generic collection interfaces
IList<T> and
IDictionary<TKey, TValue>. The type
parameter for these collections is specified by using the XML
attribute element-type for
IList<T> and the XML attributes
key-type and value-type for
IDictionary<TKey, TValue>. The values of
the collection are automaticaly converted from a string to the
appropriate type. If you are using your own user-defined type as a
generic type parameter you will likely need to register a custom type
converter. Refer to Section 5.5, “Type conversion” for
more information. The implementations of
IList<T> and
IDictionary<TKey, TValue> that is created
are System.Collections.Generic.List and
System.Collections.Generic.Dictionary.
The following class represents a lottery ticket and demonstrates how to set the values of a generic IList.
public class LotteryTicket {
List<int> list;
DateTime date;
public List<int> Numbers {
set { list = value; }
get { return list; }
}
public DateTime Date {
get { return date; }
set { date = value; }
}
}The XML fragment that can be used to configure this class is shown below
<object id="MyLotteryTicket" type="GenericsPlay.Lottery.LotteryTicket, GenericsPlay">
<property name="Numbers">
<list element-type="int">
<value>11</value>
<value>21</value>
<value>23</value>
<value>34</value>
<value>36</value>
<value>38</value>
</list>
</property>
<property name="Date" value="4/16/2006"/>
</object>The following shows the definition of a more complex class that
demonstrates the use of generics using the
Spring.Expressions.IExpression interface as the
generic type parameter for the IList element-type and the value-type
for IDictionary. Spring.Expressions.IExpression
has an associated type converter,
Spring.Objects.TypeConverters.ExpressionConverter
that is already pre-registered with Spring.
public class GenericExpressionHolder
{
private System.Collections.Generic.IList<IExpression> expressionsList;
private System.Collections.Generic.IDictionary<string,IExpression> expressionsDictionary;
public System.Collections.Generic.IList<IExpression> ExpressionsList
{
set { this.expressionsList = value; }
}
public System.Collections.Generic.IDictionary<string, IExpression> ExpressionsDictionary
{
set { this.expressionsDictionary = value; }
}
public IExpression this[int index]
{
get
{
return this.expressionsList[index];
}
}
public IExpression this[string key]
{
get { return this.expressionsDictionary[key]; }
}
}An example XML configuration of this class is shown below
<object id="genericExpressionHolder"
type="Spring.Objects.Factory.Xml.GenericExpressionHolder,
Spring.Core.Tests">
<property name="ExpressionsList">
<list element-type="Spring.Expressions.IExpression, Spring.Core">
<value>1 + 1</value>
<value>date('1856-7-9').Month</value>
<value>'Nikola Tesla'.ToUpper()</value>
<value>DateTime.Today > date('1856-7-9')</value>
</list>
</property>
<property name="ExpressionsDictionary">
<dictionary key-type="string" value-type="Spring.Expressions.IExpression, Spring.Core">
<entry key="zero">
<value>1 + 1</value>
</entry>
<entry key="one">
<value>date('1856-7-9').Month</value>
</entry>
<entry key="two">
<value>'Nikola Tesla'.ToUpper()</value>
</entry>
<entry key="three">
<value>DateTime.Today > date('1856-7-9')</value>
</entry>
</dictionary>
</property>
</object>The <null> element is used to handle
null values. Spring.NET treats empty arguments for
properties and constructor arguments as empty
string instances. The following configuration
demonstrates this behaviour...
<object type="Examples.ExampleObject, ExamplesLibrary"> <property name="email"><value></value></property> <!-- equivalent, using value attribute as opposed to nested <value/> element... <property name="email" value=""/> </object>
This results in the email property being set to the empty string
value (""), in much the same way as can be seen in
the following snippet of C# code...
exampleObject.Email = "";
The special <null/> element may be used to
indicate a null value; to wit...
<object type="Examples.ExampleObject, ExamplesLibrary"> <property name="email"><null/></property> </object>
This results in the email property being set to
null, again in much the same way as can be seen in
the following snippet of C# code...
exampleObject.Email = null;
An indexer lets you set and get values from a collection using a
familiar bracket [] notation. Spring's XML
configuration supports the setting of indexer properties. Overloaded
indexers as well as multiparameter indexers are also supported. The
property expression parser described in Chapter 11, Expression Evaluation
is used to perform the type conversion of the indexer name argument
from a string in the XML file to a matching target type. As an example
consider the following class
public class Person
{
private IList favoriteNames = new ArrayList();
private IDictionary properties = new Hashtable();
public Person()
{
favoriteNames.Add("p1");
favoriteNames.Add("p2");
}
public string this[int index]
{
get { return (string) favoriteNames[index]; }
set { favoriteNames[index] = value; }
}
public string this[string keyName]
{
get { return (string) properties[keyName]; }
set { properties.Add(keyName, value); }
}
}The XML configuration snippet to populate this object with data is shown below
<object id="person" type="Test.Objects.Person, Test.Objects">
<property name="[0]" value="Master Shake"/>
<property name="['one']" value="uno"/>
</object>![]() | Note |
|---|---|
| The use of the property expression parser in Release 1.0.2
changed how you configure indexer properties. The following section
describes this usage. The older style configuration uses the following syntax <object id="objectWithIndexer" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="Item[0]" value="my string value"/>
</object> You can also change the name used to identify
the indexer by adorning your indexer method declaration with the
attribute There are some limitations to be aware in the older indexer configuration. The indexer can only be of a single parameter that is convertible from a string to the indexer parameter type. Also, multiple indexers are not supported. You can get around that last limitation currently if you use the IndexerName attribute. |
There are also some shortcut forms that are less verbose than
using the full value and ref
elements. The property,
constructor-arg, and entry
elements all support a value attribute which may be
used instead of embedding a full value element.
Therefore, the following:
<property name="myProperty">
<value>hello</value>
</property>
<constructor-arg>
<value>hello</value>
</constructor-arg>
<entry key="myKey">
<value>hello</value>
</entry>are equivalent to:
<property name="myProperty" value="hello"/> <constructor-arg value="hello"/> <entry key="myKey" value="hello"/>
In general, when typing definitions by hand, you will probably prefer to use the less verbose shortcut form.
The property and
constructor-arg elements support a similar shortcut
ref attribute which may be used instead of a full
nested ref element. Therefore, the following...
<property name="myProperty"> <ref object="anotherObject"/> </property> <constructor-arg index="0"> <ref object="anotherObject"/> </constructor-arg>
is equivalent to...
<property name="myProperty" ref="anotherObject"/> <constructor-arg index="0" ref="anotherObject"/>
![]() | Note |
|---|---|
The shortcut form is equivalent to a <ref
object="xxx"> element; there is no shortcut for either
the <ref local="xxx"> or <ref
parent="xxx"> elements. For a local or parent
ref, you must still use the long form. |
Finally, the entry element allows a shortcut form the specify the key and/or value of a dictionary, in the form of key/key-ref and value/value-ref attributes. Therefore, the following
<entry>
<key>
<ref object="MyKeyObject"/>
</key>
<ref object="MyValueObject"/>
</entry>Is equivalent to:
<entry key-ref="MyKeyObject" value-ref="MyValueObject"/>
As mentioned previously, the equivalence is to <ref
object="xxx"> and not the local or parent forms of object
references.
Note that compound or nested property names are perfectly legal when setting object properties. Property names are interpreted using the Spring Expression Language (SpEL) and therefore can leverage its many features to set property names. For example, in this object definition a simple nested property name is configured
<object id="foo" type="Spring.Foo, Spring.Foo"> <property name="bar.baz.name" value="Bingo"/> </object>
As an example of some alternative ways to declare the property
name, you can use SpEL's support for indexers to configure a Dictionary
key value pair as an alternative to the nested
<dictionary> element. More importantly, you can
use the 'expression' element to refer to a Spring expression as the
value of the property. Simple examples of this are shown below
<property name=“minValue” expression=“int.MinValue” /> <property name=“weekFromToday” expression="DateTime.Today + 7"/>
Using SpEL's support for method evaluation, you can easily call static method on various helper classes in your XML configuraiton.
For most situations, the fact that an object is a dependency of
another is expressed by the fact that one object is set as a property of
another. This is typically accomploished with the
<ref/> element in XML-based configuration
metadata. For the relatively infrequent situations where dependencies
between objects are less direct (for example, when a static initializer
in a class needs to be triggered) the 'depends-on'
attribute may be used to explicitly force one or more objects to be
initialized before the object using this element is initialized. Find
below an example of using the 'depends-on' attribute
to express a dependency on a single object..
<object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager"> <property name="manager" ref="manager"/> </object> <object id="manager" type="Examples.ManagerObject, ExamplesLibrary"/>
If you need to express a dependency on multiple objects, you can
supply a list of object names as the value of the
'depends-on' attribute, with commas, whitespace and
semicolons all valid delimiters, like so:
<object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager,accountDao"> <property name="manager" ref="manager" /> </object> <object id="manager" type="Examples.ManagerObject, ExamplesLibrary" /> <object id="accountDao" type="Examples.AdoAccountDao, ExamplesLibrary" />
![]() | Note |
|---|---|
The ' |
The default behavior for
IApplicationContext implementations is to eagerly
pre-instantiate all singleton objects at startup. Pre-instantiation
means that an IApplicationContext will eagerly
create and configure all of its singleton objects as part of its
initialization process. Generally this is a good thing, because it means
that any errors in the configuration or in the surrounding environment
will be discovered immediately (as opposed to possibly hours or even
days down the line).
However, there are times when this behavior is not what is wanted.
If you do not want a singleton object to be pre-instantiated when using
an IApplicationContext, you can selectively
control this by marking an object definition as lazy-initialized. A
lazily-initialized object indicates to the IoC container whether or not
an object instance should be created at startup or when it is first
requested.
When configuring objects via XML, this lazy loading is controlled
by the 'lazy-init'attribute on the
<object/> element; for example:
<object id="lazy" type="MyCompany.ExpensiveToCreateObject, MyApp" lazy-init="true"/> <object name="not.lazy" type="MyCompany.AnotherObject, MyApp"/>
When the above configuration is consumed by an
IApplicationContext, the object named
'lazy' will not be eagerly pre-instantiated when the
IApplicationContext is starting up, whereas the
'not.lazy' object will be eagerly pre-instantiated.
One thing to understand about lazy-initialization is that even
though an object definition may be marked up as being lazy-initialized,
if the lazy-initialized object is the dependency of a singleton object
that is not lazy-initialized, when the
IApplicationContext is eagerly pre-instantiating
the singleton, it will have to satisfy all of the singletons
dependencies, one of which will be the lazy-initialized object! So don't
be confused if the IoC container creates one of the objects that you
have explicitly configured as lazy-initialized at startup; all that
means is that the lazy-initialized object is being injected into a
non-lazy-initialized singleton object elsewhere.
It is also possible to control lazy-initialization at the
container level by using the
'default-lazy-init'attribute on the <objects/>
element; for example:
<objects default-lazy-init="true"> <!-- no objects will be pre-instantiated... --> </objects>
The Spring container is able to autowire relationships between collaborating objects. This means that it is possible to automatically let Spring resolve collaborators (other objects) for your object by inspecting the contents of the IoC container.. The autowiring functionality has five modes. Autowiring is specified per object and can thus be enabled for some object, while other objects will not be autowired. Using autowiring, it is possible to reduce or eliminate the need to specify properties or constructor arguments, thus saving a significant amount of typing. When using XML-based configuration metadata, the autowire mode for an object definition is specified by using the autowire attribute of the <object/> element. The following values are allowed:
Table 5.3. Autowiring modes
| Mode | Explanation |
|---|---|
| no | No autowiring at all. This is the default value and you are encouraged not to change this for large applications, since specifying your collaborators explicitly gives you a feeling for what you're actually doing (always a bonus) and is a great way of somewhat documenting the structure of your system. |
| byName | This option will inspect the objects within the
container, and look for an object named exactly the same as
the property which needs to be autowired. For example, if you
have an object definition that is set to autowire by name, and
it contains a Master property, Spring.NET
will look for an object definition named
Master, and use it as the value of the
Master property on your object
definition. |
| byType | This option gives you the ability to resolve
collaborators by type instead of by name. Supposing you have
an IObjectDefinition with a
collaborator typed SqlConnection,
Spring.NET will search the entire object factory for an object
definition of type SqlConnection and
use it as the collaborator. If 0 (zero) or more than
1 (one) object definitions of the desired type exist in the
container, a failure will be reported and you won't be able to
use autowiring for that specific object. |
| constructor | This is analogous to byType, but applies to constructor arguments. If there isn't exactly one object of the constructor argument type in the object factory, a fatal error is raised. |
| autodetect | Chooses constructor or byType through introspection of the object class. If a default constructor is found, byType gets applied. |
Note that explicit dependencies in property and constructor-arg settings always override autowiring. Please also note that it is not currently possible to autowire so-called simple properties such as primitives, Strings, and Types (and arrays of such simple properties). (This is by-design and should be considered a feature.) When using either the byType or constructor autowiring mode, it is possible to wire arrays and typed-collections. In such cases all autowire candidates within the container that match the expected type will be provided to satisfy the dependency. Strongly-typed IDictionaries can even be autowired if the expected key type is string. An autowired IDictionary values will consist of all object instances that match the expected type, and the IDictionary's keys will contain the corresponding object names.
Autowire behavior can be combined with dependency checking, which will be performed after all autowiring has been completed. It is important to understand the various advantages and disadvantages of autowiring. Some advantages of autowiring include:
Autowiring can significantly reduce the volume of configuration required. However, mechanisms such as the use of a object template (discussed elsewhere in this chapter) are also valuable in this regard.
Autowiring can cause configuration to keep itself up to date as your objects evolve. For example, if you need to add an additional dependency to a class, that dependency can be satisfied automatically without the need to modify configuration. Thus there may be a strong case for autowiring during development, without ruling out the option of switching to explicit wiring when the code base becomes more stable.
Some disadvantages of autowiring:
Autowiring is more magical than explicit wiring. Although, as noted in the above table, Spring is careful to avoid guessing in case of ambiguity which might have unexpected results, the relationships between your Spring-managed objects are no longer documented explicitly.
Wiring information may not be available to tools that may generate documentation from a Spring container.
Another issue to consider when autowiring by type is that multiple object definitions within the container may match the type specified by the setter method or constructor argument to be autowired. For arrays, collections, or IDictionary, this is not necessarily a problem. However for dependencies that expect a single value, this ambiguity will not be arbitrarily resolved. Instead, if no unique object definition is available, an Exception will be thrown.
When deciding whether to use autowiring, there is no wrong or right answer in all cases. A degree of consistency across a project is best though; for example, if autowiring is not used in general, it might be confusing to developers to use it just to wire one or two object definitions.
Spring.NET has the ability to try to check for the existence of unresolved dependencies of an object deployed into the container. These are properties of the object, which do not have actual values set for them in the object definition, or alternately provided automatically by the autowiring feature.
This feature is sometimes useful when you want to ensure that all
properties (or all properties of a certain type) are set on an object.
Of course, in many cases an object class will have default values for
many properties, or some properties do not apply to all usage scenarios,
so this feature is of limited use. Dependency checking can also be
enabled and disabled per object, just as with the autowiring
functionality. The default dependency checking mode is to
not check dependencies. Dependency checking can be
handled in several different modes. In XML-based configuration, this is
specified via the dependency-check attribute in an
object definition, which may have the following values.
Table 5.4. Dependency checking modes
| Mode | Explanation |
|---|---|
| none | No dependency checking. Properties of the object which have no value specified for them are simply not set. |
| simple | Dependency checking is done for primitive types and collections (this means everything except collaborators). |
| object | Dependency checking is done for collaborators only. |
| all | Dependency checking is done for collaborators, primitive types and collections. |
For most users, the majority of the objects in the container will be singletons. When a singleton object needs to collaborate with (use) another singleton object, or a non-singleton object needs to collaborate with another non-singleton object, the typical and common approach of handling this dependency (by defining one object to be a property of the other) is quite adequate. There is however a problem when the object lifecycles are different. Consider a singleton object A which needs to use a non-singleton (prototype) object B, perhaps on each method invocation on A. The container will only create the singleton object A once, and thus only get the opportunity to set its properties once. There is no opportunity for the container to provide object A with a new instance of object B every time one is needed.
One solution to this problem is to forego some inversion of
control. Object A can be made aware of the
container by implementing the
IObjectFactoryAware interface, and use programmatic means to ask
the container via a GetObject("B") call for (a
typically new) object B every time it needs it. Find below an admittedly
somewhat contrived example of this approach
using System.Collections;
using Spring.Objects.Factory;
namespace Fiona.Apple
{
public class CommandManager : IObjectFactoryAware
{
private IObjectFactory objectFactory;
public object Process(IDictionary commandState)
{
// grab a new instance of the appropriate Command
Command command = CreateCommand();
// set the state on the (hopefully brand new) Command instance
command.State = commandState;
return command.Execute();
}
// the Command returned here could be an implementation that executes asynchronously, or whatever
protected Command CreateCommand()
{
return (Command) objectFactory.GetObject("command"); // notice the Spring API dependency
}
public IObjectFactory ObjectFactory
{
set { objectFactory = value; }
}
}
}The above example is generally not a desirable solution since the business code is then aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, allows this use case to be handled in a clean fashion.
Lookup method injection refers to the ability of the container
to override abstract or concrete methods on
container managed objects, to return the result
of looking up another named object in the container. The lookup will
typically be of a prototype object as in the scenario described above.
The Spring framework implements this method injection by a dynamically
generating a subclass overriding the method using the classes in the
System.Reflection.Emit namespace. You can read more
about the motivation for Method Injection in this blog
entry.
So if you look at the code from the previous code snipped (the
CommandManager class), the Spring container is
going to dynamically override the implementation of the
CreateCommand() method. Your
CommandManager class is not going to have any
Spring dependencies, as can be seen in this reworked example
below:
using System.Collections;
namespace Fiona.Apple
{
public abstract class CommandManager
{
public object Process(IDictionary commandState)
{
Command command = CreateCommand();
command.State = commandState;
return command.Execute();
}
// okay... but where is the implementation of this method?
protected abstract Command CreateCommand();
}
}In the client class containing the method to be injected (the CommandManager in this case) the method definition must observe the following form:
<public|protected> [abstract] <return-type> TheMethodName(no-arguments);
If the method is abstract, the
dynamically-generated subclass will implement the method. Otherwise,
the dynamically-generated subclass will override the concrete method
defined in the original class. Let's look at an example:
<!-- a stateful object deployed as a prototype (non-singleton) --> <object id="command" class="Fiona.Apple.AsyncCommand, Fiona" singleton="false"> <!-- inject dependencies here as required --> </object> <!-- commandProcessor uses a statefulCommandHelpder --> <object id="commandManager" type="Fiona.Apple.CommandManager, Fiona"> <lookup-method name="CreateCommand" object="command"/> </object>
The object identified as commandManager will
call its own method CreateCommand whenever it needs
a new instance of the command object. It is
important to note that the person deploying the objects must be
careful to deploy the command object as prototype
(if that is actually what is needed). If it is deployed as a singleton the same
instance of singleShotHelper will be returned each
time!
Note that lookup method injection can be combined with Constructor Injection (supplying optional constructor arguments to the object being constructed), and also with Setter Injection (settings properties on the object being constructed).
A less commonly useful form of method injection than Lookup Method Injection is the ability to replace arbitrary methods in a managed object with another method implementation. Users may safely skip the rest of this section (which describes this somewhat advanced feature), until this functionality is actually needed.
In an XmlObjectFactory, the
replaced-method element may be used to replace an
existing method implementation with another. Consider the following
class, with a method ComputeValue, which we want to
override:
public class MyValueCalculator {
public virtual string ComputeValue(string input) {
// ... some real code
}
// ... some other methods
}A class implementing the
Spring.Objects.Factory.Support.IMethodReplacer
interface is needed to provide the new (injected) method
definition.
/// <summary>
/// Meant to be used to override the existing ComputeValue(string)
/// implementation in MyValueCalculator.
/// </summary>
public class ReplacementComputeValue : IMethodReplacer
{
public object Implement(object target, MethodInfo method, object[] arguments)
{
// get the input value, work with it, and return a computed result...
string value = (string) arguments[0];
// compute...
return result;
}
}The object definition to deploy the original class and specify the method override would look like this:
<object id="myValueCalculator" type="Examples.MyValueCalculator, ExampleAssembly">
<!-- arbitrary method replacement -->
<replaced-method name="ComputeValue" replacer="replacementComputeValue">
<arg-type match="String"/>
</replaced-method>
</object>
<object id="replacementComputeValue" type="Examples.ReplacementComputeValue, ExampleAssembly"/>One or more contained arg-type elements
within the replaced-method element may be used to
indicate the method signature of the method being overridden. Note
that the signature for the arguments is actually only needed in the
case that the method is actually overloaded and there are multiple
variants within the class. For convenience, the type string for an
argument may be a substring of the fully qualified type name. For
example, all the following would match
System.String.
System.String
String
StrSince the number of arguments is often enough to distinguish between each possible choice, this shortcut can save a lot of typing, by just using the shortest string which will match an argument.
This section details those configuration scenarios that involve
the setting of properties and constructor arguments using the members of
other objects and classes. This kind of scenario is quite common,
especially when dealing with legacy classes that you cannot (or won't)
change to accommodate some of Spring.NET's conventions... consider the
case of a class that has a constructor argument that can only be
calculated by going to say, a database. The
MethodInvokingFactoryObject handles exactly this
scenario ... it will allow you to inject the result of an arbitrary
method invocation into a constructor (as an argument) or as the value of
a property setter. Similarly,
PropertyRetrievingFactoryObject and
FieldRetrievingFactoryObject allow you to
retrieve values from another object's property or field value. These
classes implement the IFactoryObject interface
which indicates to Spring.NET that this object is itself a factory and
the factories product, not the factory itself, is what will be
associated with the object id. Factory objects are discussed further in
???