The Spring.Core assembly provides the basis for
the Spring.NET Inversion of Control (IoC - sometimes also referred to as
Dependency Injection) features (see Section 3.1, “Inversion of Control” for
some additional material describing this software engineering principle).
The IObjectFactory
interface from the Spring.Core assembly provides an
advanced configuration mechanism capable of managing objects of any
nature, using potentially any kind of storage facility. The
IApplicationContext
interface from the same assembly builds on top of the functionality
provided by the IObjectFactory interface,
complementing it with features such as integration with Spring.NET's
Aspect Oriented Programming (AOP) features and message resource handling
(for use in internationalization).
In short, the IObjectFactory provides the
configuration framework and basic functionality, while the
IApplicationContext adds more enterprise-centric
functionality to it. In general, 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 27, 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 container
that instantiates, configures, and manages a number of objects. These
objects typically collaborate with one another, and thus can be said to
have dependencies between themselves. These dependencies are reflected
in the configuration data used by the
IObjectFactory (although some dependencies may
not be visible as configuration data, but rather be a function of
programmatic interactions between objects at runtime).
An IObjectFactory is represented by the
Spring.Objects.Factory.IObjectFactory interface,
of which there are multiple implementations. The most commonly used
simple IObjectFactory is the
Spring.Objects.Factory.Xml.XmlObjectFactory.
Interaction with the IObjectFactory interface is
discussed in Section 4.7, “Interacting with the IObjectFactory”. Additional
features offered by the IApplicationContext are
discussed in section Section 4.11, “Introduction to the
IApplicationContext”.
As mentioned previously in the introduction, one of the central
tenets of the Spring.NET framework is non-invasiveness. Quite simply,
your application code should not depend on any of the Spring.NET APIs.
However, if one is going to take advantage of the features provided by
Spring.NET's IoC container, through the use of either the
IObjectFactory or the
Spring.Context.IApplicationContext interfaces, at
some point one has to instantiate an
appropriate implementation of either of these two core interfaces. This
can happen explicitly in user code via the use of the
new operator (in C# - VB.NET developers have the
equivalent New operator); or more easily 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.
What follows are some examples of how one can instantiate an
actual implementation of the IObjectFactory
interface. In the following example an object factory, complete with
object definitions describing the services that we want to wire up and
expose, is created from the contents of the
objects.xml file; this file is passed in as an
argument to one of the constructors of the
XmlObjectFactory class.
[C#]
IResource input = new FileSystemResource ("objects.xml");
IObjectFactory factory = new XmlObjectFactory(input);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.Streams. The
IResource abstraction is explained further in
Section 6.1, “Introduction”. 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.
As previously mentioned, one is more likely to use the
IApplicationContext interface. Any of the
IApplicationContext implementations can be
instantiated explicitly by specifying IResource
URI locations to the constructor. Multiple configuration files can used
to construct an IApplicationContext as shown
below.
IApplicationContext context = new XmlApplicationContext( "file://objects.xml", "assembly://MyAssembly/MyProject/objects-dal-layer.xml"); // of course, an IApplicationContext is also an IObjectFactory... IObjectFactory factory = (IObjectFactory) context;
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 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://objects.xml"/>
<resource uri="assembly://MyAssembly/MyProject/objects-dal-layer.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://objects.xml"/>
<resource uri="assembly://MyAssembly/MyProject/objects-dal-layer.xml"/>
</context>
</spring> To acquire a reference to an
IApplicationContext using a custom configuration
section, one simply uses the following code; please note that the string
literal 'spring/context' is not arbitrary... you
must use this string value (a constant
is available in the AbstractApplicationContext
class).
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 4.17, “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.
An IObjectFactory configuration consists
of, at its most basic level, definitions of one or more objects that the
IObjectFactory will manage. In an XML based
factory these are defined as one or more object
elements inside a top-level objects element. The
top-level name must be objects.
<objects xmlns="http://www.springframework.net" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd"> <object id="..." type="..."> ... </object> <object id="...." type="..."> ... </object> ... </objects>
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 peak inside (see Appendix A, 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 VisualStudio.NET) by
providing validation (and Intellisense support in the case of
VisualStudio.NET). You may wish to refer to Chapter 26, Visual Studio.NET Integration for
more information regarding such integration. You can also obtain the XSD
that supports the latest release from the web at spring-objects.xsd.
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 4.13.1, “Context Hierarchies” and Section 4.15, “Importing Object Definitions from One File Into Another”.
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 4.12, “Configuration of IApplicationContext”
Object definitions describe objects managed by an
IObjectFactory or
IApplicationContext. Object definitions contain
the following information:
The object type, which is the actual implementation class
(the .NET System.Type) of the object being
described in the object definition.
Object behavioral configuration elements, which state how the object should behave in the Spring.NET IoC container (i.e. prototype or singleton, autowiring mode, dependency checking mode, initialization and destruction methods).
Property values 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 (either specified as a property or as a constructor argument), or the System.Type that should be used to create the thread pool.
Other objects your object needs to do its work, i.e. collaborators (also specified as properties or as constructor arguments). These can also be called dependencies.
In the list above, we mentioned the use of property setters and constructor arguments. Spring.NET supports two types of IoC: Type 2 and Type 3 (Constructor Dependency Injection and Setter Dependency Injection respectively). What that basically means is that when new objects are constructed by the IoC container you can set properties of the object using both regular property setters, and also directly as arguments that you specify to a constructor.
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 4.1. Object definition explanation
| Feature | More info |
|---|---|
| type | Section 4.2.3, “Object Creation” |
| id and name | Section 4.2.5, “The object identifiers (id and
name)” |
| singleton or prototype | Section 4.2.6, “Singleton & Prototype Scope” |
| object properties | Section 4.3.1, “Setting object properties and collaborators” |
| constructor arguments | Section 4.3.1, “Setting object properties and collaborators” |
| autowiring mode | Section 4.3.8, “Autowiring collaborators” |
| dependency checking mode | Section 4.3.9, “Checking for dependencies” |
| initialization method | Section 4.5.1, “Lifecycle interfaces” |
| destruction method | Section 4.5.1, “Lifecycle interfaces” |
Every object definition needs to know the type (the .NET
System.Type) of the object being defined ( see
Section 4.2.3.3, “Object creation via an instance factory method”, and
Section 4.6, “Abstract and Child object definitions”for the exceptions). In the much
more common case where the IObjectFactory itself
directly creates the object instance by calling one of the objects
constructors the type attribute specifies the type of the object that is
to be instantiated. In the less common case where the
IObjectFactory calls a so-called factory method on a
type to create the object instance, the type attribute specifies the
actual type containing the factory method. The type of the object
returned from the invocation of this factory method may be the same
type, or another type entirely, it doesn't matter.
When creating an object using the constructor approach, there are no special requirements as to what this class is or how it is implemented (i.e. your class does not have to implement a special interface to make it Spring.NET compatible), other than the fact that it must not be an interface. Just specifying the object type (and the assembly into which it has been compiled) 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"/>
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 [1] 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.
Quite similar to using a static factory method to create an object, is the the use of an instance (non-static) factory method, where a factory method of an existing object from the factory is called 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 an ancestor object factory which contains the factory method. The factory method itself should still be set via the factory-method attribute.
Following is an example...
<!-- the factory object, which contains an instance method called 'CreateInstance' -->
<object id="exampleFactory" type="..."/>
<!-- 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.
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 seperate 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.candidate
<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 4.12, “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 snippit 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 snippit 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>
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 IObjectFactory or
IApplicationContext that the object definition is
hosted in. An object definition will almost always have only one id, but
if an object has more than one id, the extra ones can essentially be
considered aliases.
In an XML object definition, the id or
name attributes of the object
element are used to specify the object definition's id (s), and at least
one id must be specified in one or both of these attributes. The
id attribute allows you to specify one id, and since
it is marked in the Spring.NET XSD as a bona-fide XML element ID
attribute, the parser is able to do some extra validation when other
elements point back to this one. As such, it is the preferred way to
specify an object id. However, the XML specification does limit the
characters that are legal in XML IDs. This is usually not a constraint,
but if you have a need to use one of these characters, or want to
introduce aliases to the object, you may also or instead specify one or
more object ids (separated by a comma (,) or semicolon (;)) via the
name attribute.
Objects can be deployed in one of two modes: singleton or non-singleton (the latter is also called a prototype, although the term is used loosely as it doesn't quite fit). When an object definition is set to the singleton mode, only one shared instance of the object will be managed, and all requests for objects with an id or ids matching that object definition will result in that one specific object instance being returned (i.e. the object defined by the object definition will only ever be created once).
The non-singleton, prototype mode of an object deployment results in the creation of a new object instance every time a request for that specific object is received. For example, this is ideal for those situations where each user needs an independent user object or something similar.
Object definitions operate in singleton mode by default, unless you specify otherwise. Keep in mind that by changing the mode to non-singleton (prototype), each request for an object will result in a new instance and this might not be what you actually want. So only change the mode to prototype when absolutely necessary.
![]() | Note |
|---|---|
When deploying an object in the prototype mode, the lifecycle of the object changes slightly. By definition, Spring.NET cannot manage the complete lifecycle of a non-singleton / prototype object, since after it is created, it is given to the client and the container does not keep track of it at all any longer. You can think of Spring.NET's role when talking about a non-singleton / prototype object as a replacement for the 'new' operator. Any lifecycle aspects past that point have to be handled by the client. The lifecycle of an object in the
IObjectFactory
is further described in
Section 4.5.1, “Lifecycle interfaces” |
In the XML fragment below, two objects are declared of which one
is defined as a singleton, and the other one as a prototype. The
exampleObject object is created each and every time a
client asks the IObjectFactory for this object,
whereas the yetAnotherExample object is only created
once; a reference to the exact same instance is returned on each request
for this object.
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary" singleton="false"/> <object name="yetAnotherExample" type="Examples.ExampleObjectTwo, ExamplesLibrary" singleton="true"/>
The lazy-init attribute gives you control over
when the singleton is instantiated. A common variation on the singleton
design pattern is to lazily create the object, that is to create it only
when it is first used. By default, the lazy-init
attribute is set to false instructing the IoC container to
pre-instantiate singletons object when the container is initialized.
Setting the attribute to false will delay the creation until the object
is first requested from the container, either directly from a user
request or as a result of the resolving object dependencies.
Inversion of Control has already been referred to as
Dependency Injection. The basic principle is that
objects define their dependencies (i.e. 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) of the object instantiating or locating its
dependencies on its own using direct construction of classes (via the
new operator), or something like the
Service Locator pattern. While we will not
elaborate too much on the advantages of Dependency Injection, it becomes
evident upon usage that code gets much cleaner and reaching a higher
grade of decoupling is much easier when objects do not look up their
dependencies, but are provided them, and additionally do not even know
where the dependencies are located and of what actual type they
are.
As touched on in the previous paragraph, Inversion of Control / Dependency Injection exists in two major variants:
setter-based dependency injection is realized by calling property setters on your objects after invoking a no-argument constructor to instantiate your object. Spring.NET generally advocates the usage of setter-based dependency injection, since a large number of constructor arguments can get unwieldy, especially when some properties are optional.
constructor-based dependency injection is realized by invoking a constructor with a number of arguments, each representing a collaborator or property. Although Spring.NET generally advocates the usage of setter-based dependency injection as much as possible, it does fully support the constructor-based approach as well, since you may wish to use it with pre-existing objects which provide only multi-argument constructors, and no setters. Additionally, for simpler objects, some people prefer the constructor approach as a means of ensuring objects can not be constructed in an invalid state.
The IObjectFactory supports both of these
variants for injecting dependencies into objects it manages. 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 (i.e. programmatically), but
rather with an XML definition file which will be converted internally
into instances of these classes, and used to load an entire
IObjectFactory or
IApplicationContext.
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 or constructor arguments. These will be provided to the object, when the object is actually created.
Each property or constructor-arg is either an actual
definition of the value to set, or a reference to another object
in the IObjectFactory. In the case of the
IApplicationContext, the reference can be
to an object in a parent
IApplicationContext.
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.
Additionally, when talking about the XML based
IObjectFactory variants (including the
IApplicationContext variants), these have
built-in support for defining IList,
IDictionary, and Set
collection types. Spring.NET uses
TypeConverter definitions to be able to
convert string values to other, arbitrary types. Refer to Section 5.3, “Type conversion” for more information
regarding type conversion, and how you can design your classes to
be convertible by Spring.NET.
It is important to realize that Spring.NET validates the
configuration of each object in the
IObjectFactory when the
IObjectFactory is created, including the
validation that properties that are object references are actually
referring to valid objects (i.e. the objects being referred to are
also defined in the IObjectFactory, or in a
parent context in the case of
IApplicationContext). However, the object
properties themselves are not set until the object is
actually created. For objects that have been defined as
singletons and set to be pre-instantiated (such as singleton
objects in an IApplicationContext),
creation happens at the time that the
IObjectFactory 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 pick up configuration issues, including references to
non-existent object definitions and circular dependencies, at
IObjectFactory load-time. It will actually
set properties and resolve dependencies (i.e. create any dependent
objects if needed) as late as possible, which is when the object
is actually created. This does mean that an
IObjectFactory 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).
Some examples (using XML based object definitions)...
First, an example of using the
IObjectFactory for setter-based dependency
injection. Below is a small part of an XML file specifying some object
definitions. Following is the code for the actual main object itself,
showing the appropriate setters declared.
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary">
<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; }
}
}Now, an example of using the IObjectFactory
for IoC Type 3 (Constructor Dependency Injection). Below is a snippet
from an XML configuration that specifies constructor arguments and the
actual object code, showing the constructor:
<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 Class As 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.
Note that Type 2 Setter Injection and Type 3 Constructor Injection IoC are 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; }
}
}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.
Constructor argument resolution matching occurs using the
argument's type. When another object is referenced, the type is known,
and matching can occur. When a simple type is used, such as
<value>1</value>, Spring.NET cannot
determine the type of the value, and so cannot match by type without
help. Consider the following class, which is used for the following two
sections:
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;
}
public string UltimateAnswer
{
get { return this.ultimateAnswer; }
}
public int Years
{
get { return this.years; }
}
}
}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 4.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>
As mentioned in the previous section, object properties and
constructor arguments can be defined as either references to other
managed objects (collaborators). The
XmlObjectFactory (located in the
Spring.Objects.Factory.Xml namespace) supports a
number of sub-element types within its property and
constructor-arg elements for this purpose.
The value element specifies a property or
constructor argument as a human-readable string representation. As
mentioned in detail 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>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;
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 | 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 4.3.3.8, “Value and ref shortcut forms” for more information.
Please be advised that the setting of multiple values for a
NameValueCollection is planned
for a future release.
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 4.4, “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>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 10, 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 snippit 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 convertable 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. |
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>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"/>
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, as long as all components of the path except the final property name are non-null. For example, in this object definition:
<object id="foo" type="Spring.Foo, Spring.Foo"> <property name="bar.baz.name" value="Bingo"/> </object>
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 aware of the container by implementing the
IObjectFactoryAware interface, and use
programmatic means to ask the container via a
GetObject("B") call for (a new) object B every time
it needs it. This is generally not a desirable solution since the object
code is then aware of and coupled to Spring.NET.
Method Injection, an advanced feature of supporting
IObjectFactoryAware implementations, allows this
use case to be handled in a clean fashion, along with some other
scenarios.
Lookup method injection refers to the ability of the container
to override abstract or concrete methods on managed
objects in the container, and to return the result of looking up
another named object in the container. The lookup will typically be of
a non-singleton object as per the scenario described above (although
it can also be a singleton). Spring.NET implements this through a
dynamically generated subclass overriding the method using the classes
in the System.Reflection.Emit namespace.
In the client class containing the method to be injected, the method definition must observe the following form:
protected abstract SingleShotHelper CreateSingleShotHelper();
If the method is not abstract, Spring.NET
will simply override the existing implementation. In the
XmlObjectFactory case, you instruct Spring.NET
to inject / override this method to return a particular object from
the container, by using the lookup-method element
inside the object definition. For example:
<!-- a stateful object deployed as a prototype (non-singleton) -->
<object id="singleShotHelper" class="..." singleton="false"/>
<!-- myobject uses singleShotHelper -->
<object id="myObject" type="...">
<lookup-method name="CreateSingleShotHelper" object="singleShotHelper"/>
<property>
...
</property>
</object>The object identified as myObject will call
its own method CreateSingleShotHelper whenever it
needs a new instance of the singleShotHelper
object. It is important to note that the person deploying the objects
must be careful to deploy the singleShotHelper
object as a non-singleton (if that is actually what is needed). If it
is deployed as a singleton (either explicitly, or relying on the
default true setting for this flag), 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 attendant IObjectFactory definition
to deploy the original class and specify the method override would
look like:
<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 contructor 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. Similary,
PropertyRetrievingFactoryObject and
FieldRetrievingFactoryObject allow you to
retrieve values from another objects 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 futher in
Section 4.5.3, “IFactoryObject”
The PropertyRetrievingFactoryObject is an
IFactoryObject that addresses the scenario of
setting one of the properties and / or constructor arguments of an
object to the value of a property exposed on another object or class.
One can use it to get the value of any public property exposed on either an instance
or a class (in the case of a property exposed on a class, the property
must obviously be static).
In the case of a property exposed on an instance, the target
object that a PropertyRetrievingFactoryObject
will evaluate can be either an object instance specified directly
inline or a reference to another arbitrary object. In the case of a
static property exposed on a class, the target object will be the
class (the .NET System.Type) exposing the
property.
The result of evaluating the property lookup may then be used in
another object definition as a property value or constructor argument.
Note that nested properties are supported for both instance and class
property lookups. The IFactoryObject is
discussed more generally in Section 4.5.3, “IFactoryObject”.
Here's an example where a property path is used against another object instance. In this case, an inner object definition is used and the property path is nested, i.e. spouse.age.
<object name="person" type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="age" value="20"/>
<property name="spouse">
<object type="Spring.Objects.TestObject, Spring.Core.Tests">
<property name="age" value="21"/>
</object>
</property>
</object>
// will result in 21, which is the value of property 'spouse.age' of object 'person'
<object name="theAge" type="Spring.Objects.Factory.Config.PropertyRetrievingFactoryObject, Spring.Core">
<property name="TargetObject" ref="person"/>
<property name="TargetProperty" value="spouse.age"/>
</object>An example of using a
PropertyRetrievingFactoryObject to evaluate a
static property is shown below.
<object id="cultureAware"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests">
<property name="culture" ref="cultureFactory"/>
</object>
<object id="cultureFactory"
type="Spring.Objects.Factory.Config.PropertyRetrievingFactoryObject, Spring.Core">
<property name="StaticProperty">
<value>System.Globalization.CultureInfo.CurrentUICulture, Mscorlib</value>
</property>
</object>Similarly, an example showing the use of an instance property is shown below.
<object id="instancePropertyCultureAware"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests">
<property name="Culture" ref="instancePropertyCultureFactory"/>
</object>
<object id="instancePropertyCultureFactory"
type="Spring.Objects.Factory.Config.PropertyRetrievingFactoryObject, Spring.Core">
<property name="TargetObject" ref="instancePropertyCultureAwareSource"/>
<property name="TargetProperty" value="MyDefaultCulture"/>
</object>
<object id="instancePropertyCultureAwareSource"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"/>
The FieldRetrievingFactoryObject class
addresses much the same area of concern as the
PropertyRetrievingFactoryObject described in
the previous section. However, as its name might suggest, the
FieldRetrievingFactoryObject class is concerned
with looking up the value of a public
field exposed on either an instance or a class (and similarly, in the
case of a field exposed on a class, the field must obviously be
static).
The following example demonstrates using a
FieldRetrievingFactoryObject to look up the
value of a (public, static) field exposed on a class
<object id="withTypesField"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests">
<property name="Types" ref="emptyTypesFactory"/>
</object>
<object id="emptyTypesFactory"
type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core">
<property name="TargetType" value="System.Type, Mscorlib"/>
<property name="TargetField" value="EmPTytypeS"/>
</object>
The example in the next section demonstrates the look up of a (public) field exposed on an object instance.
<object id="instanceCultureAware"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests">
<property name="Culture" ref="instanceCultureFactory"/>
</object>
<object id="instanceCultureFactory"
type="Spring.Objects.Factory.Config.FieldRetrievingFactoryObject, Spring.Core">
<property name="TargetObject" ref="instanceCultureAwareSource"/>
<property name="TargetField" value="Default"/>
</object>
<object id="instanceCultureAwareSource"
type="Spring.Objects.Factory.Xml.XmlObjectFactoryTests+MyTestObject, Spring.Core.Tests"/>
The MethodInvokingFactoryObject rounds
out the trio of classes that permit the setting of properties and
constructor arguments using the members of other objects and classes.
Whereas the PropertyRetrievingFactoryObject and
FieldRetrievingFactoryObject classes dealt with
simply looking looking up and returning the value of property or field
on an object or class, the
MethodInvokingFactoryObject allows one to set a
constructor or property to the return value of an arbitrary method
invocation,
The MethodInvokingFactoryObject class
handles both the case of invoking an (instance) method on another
object in the container, and the case of a static method call on an
arbitrary class. Additonally, it is sometimes neccessary to invoke a
method just to perform some sort of initialization.... while the
mechanisms for handling object initialization have yet to be
introduced (see Section 4.5.1.1, “IInitializingObject / init-method”), these
mechanisms do not permit any arguments to be passed to any
initialization method, and are confined to invoking an initialization
method on the object that has just been instantiated by the container.
The MethodInvokingFactoryObject allows one to
invoke pretty much any method on any
object (or class in the case of a static method).
The following example (in an XML based
IObjectFactory definition) uses the
MethodInvokingFactoryObject class to force a
call to a static factory method prior to the instantiation of the
object...
<object id="force-init"
type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="StaticMethod">
<value>ExampleNamespace.ExampleInitializerClass.Initialize</value>
</property>
</object>
<object id="myService" depends-on="force-init"/> Note
that the definition for the myService object has
used the depends-on attribute to refer to the
force-init object, which will force the
initialization of the force-init object first (and
thus the calling of its configured StaticMethod
static initializer method, when myService is first
initialized. Please note that in order to effect this initialization,
the MethodInvokingFactoryObject object
must be operating in
singleton mode (the default.. see the next
paragraph).
Note that since this class is expected to be used primarily for
accessing factory methods, this factory defaults to operating in
singleton mode. As such, as soon as all of the
properties for a MethodInvokingFactoryObject
object have been set, and if the
MethodInvokingFactoryObject object is still in
singleton mode, the method will be invoked
immediately and the return value cached for later access. The first
request by the container for the factory to produce an object will
cause the factory to return the cached return value for the current
request (and all subsequent requests). The
IsSingleton property may be set to false, to
cause this factory to invoke the target method each time it is asked
for an object (in this case there is obviously no caching of the
return value).
A static target method may be specified by setting the
targetMethod property to a string
representing the static method name, with
TargetType specifying the
Type that the static method is defined on.
Alternatively, a target instance method may be specified, by setting
the TargetObject property to the name of
another Spring.NET managed object definition (the target object), and
the TargetMethod property to the name of the
method to call on that target object.
Arguments for the method invocation may be specified in two ways
(or even a mixture of both)... the first involves setting the
Arguments property to the list of arguments
for the method that is to be invoked. Note that the ordering of these
arguments is significant... the order of the values passed to the
Arguments property must be the same as the
order of the arguments defined on the method signature, including the
argumentType. This is shown in the example
below
<object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="TargetType" value="Whatever.MyClassFactory, MyAssembly"/>
<property name="TargetMethod" value="GetInstance"/>
<!-- the ordering of arguments is significant -->
<property name="Arguments">
<list>
<value>1st</value>
<value>2nd</value>
<value>and 3rd arguments</value>
<!-- automatic Type-conversion will be performed prior to invoking the method -->
</list>
</property>
</object>The second way involves passing an arguments dictionary to the
NamedArguments property... this dictionary
maps argument names (Strings) to argument
values (any object). The argument names are not case-sensitive, and
order is (obviously) not significant (since dictionaries by definition
do not have an order). This is shown in the example below
<object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="TargetObject">
<object type="Whatever.MyClassFactory, MyAssembly"/>
</property>
<property name="TargetMethod" value="Execute"/>
<!-- the ordering of named arguments is not significant -->
<property name="NamedArguments">
<dictionary>
<entry key="argumentName"><value>1st</value></entry>
<entry key="finalArgumentName"><value>and 3rd arguments</value></entry>
<entry key="anotherArgumentName"><value>2nd</value></entry>
</dictionary>
</property>
</object>The following example shows how use
MethodInvokingFactoryObject to call an instance
method.
<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" /> <object id="myObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core"> <property name="TargetObject" ref="myMethodObject"/> <property name="TargetMethod" value="Execute"/> </object>
The above example could also have been written using an anonymous inner object definition... if the object on which the method is to be invoked is not going to be used outside of the factory object definition, then this is the preferred idiom because it limits the scope of the object on which the method is to be invoked to the surrounding factory object.
Finally, if you want to use
MethodInvokingFactoryObject in conjunction with
a method that has a variable length argument list, then please note
that the variable arguments need to be passed (and configured) as a
list. Let us consider the following method
definition that uses the params keyword (in
C#), and its attendant (XML) configuration...
[C#]
public class MyClassFactory
{
public object CreateObject(Type objectType, params string[] arguments)
{
return ... // implementation elided for clarity...
}
}
<object id="myMethodObject" type="Whatever.MyClassFactory, MyAssembly" />
<object id="paramsMethodObject" type="Spring.Objects.Factory.Config.MethodInvokingFactoryObject, Spring.Core">
<property name="TargetObject" ref="myMethodObject"/>
<property name="TargetMethod" value="CreateObject"/>
<property name="Arguments">
<list>
<value>System.String</value>
<!-- here is the 'params string[] arguments' -->
<list>
<value>1st</value>
<value>2nd</value>
</list>
</list>
</object>In addition to
PropertyRetrievingFactoryObject,
MethodInvokingFactoryObject, and
FieldRetrievingFactoryObject Spring.NET comes
with other useful implementations of the
IFactoryObject interface. These are discussed
below.
The Log4NetFactoryObject is useful when
you would like to share a logging instance across a number of classes
instead of creating a logging instance per class or class hierarchy.
In the example shown below the same logging instance, with a log4Net
name of "DAOLogger", is used in both the SimpleAccountDao and
SimpleProducDao data access objects.
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net
http://www.springframework.net/xsd/spring-objects.xsd" >
<object name="daoLogger" type="Spring.Objects.Factory.Config.Log4NetFactoryObject, Spring.Core">
<property name="logName" value="DAOLogger"/>
</object>
<object name="productDao" type="PropPlayApp.SimpleProductDao, PropPlayApp ">
<property name="maxResults" value="100"/>
<property name="dbConnection" ref="myConnection"/>
<property name="log" ref="daoLogger"/>
</object>
<object name="accountDao" type="PropPlayApp.SimpleAccountDao, PropPlayApp ">
<property name="maxResults" value="100"/>
<property name="dbConnection" ref="myConnection"/>
<property name="log" ref="daoLogger"/>
</object>
<object name="myConnection" type="System.Data.Odbc.OdbcConnection, System.Data">
<property name="connectionstring" value="dsn=MyDSN;uid=sa;pwd=myPassword;"/>
</object>
</objects>Spring.NET uses the <ref/> element to
express references to other dependant objects. Unless you have some
special initialization requirements, you don't have to use the
<depends-on/> element. However, when you are
using statics that need initialization or some object needs to be
initialized because of something else needing preparation, you can use
the <depends-on/> element. This will ensure all
objects you've listed as dependencies will get initialized before
they're actually set on the object. For example...
<object id="objectOne" type="Examples.ExampleObject, ExamplesLibrary" depends-on="manager"> <property name="manager" ref="manager"/> </object> <object id="manager" type="ManagerObject"/>
Spring.NET has autowire capabilities, meaning that it is possible to automatically let Spring.NET resolve collaborators (other objects) for your object by inspecting the object definitions in the container. Autowiring is specified per object and can thus be enabled for some objects, while other objects won't be autowired. Using autowiring, it is possible to reduce or even wholly eliminate the need to specify properties or constructor arguments. [2] The autowiring functionality has five modes...
Table 4.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 |
|---|---|
| Explicit dependencies always override autowiring. Autowire behavior can be combined with dependency checking, which will be performed after all autowiring has been completed. |
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 4.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. |
Type converters are responsible for converting objects from one type
to another. When using the XML based file to configure the IoC container,
string based property values are converted to the target property type.
Spring will rely on the standard .NET support for type conversion unless
an alternative TypeConverter is registered for a
given type. How to register custom TypeConverters will be described
shortly. As a reminder, the standard .NET type converter support works by
associating a TypeConverter attribute with the
class definition by passing the type of the converter as an attribute
argument. [3] For example, an abbreviated class definition for the BCL
type Font is shown below.
[Serializable, TypeConverter(typeof(FontConverter)), ...]
public sealed class Font : MarshalByRefObject, ICloneable, ISerializable, IDisposable
{
// Methods
... etc ..
}The default type converter for enumerations is the
System.ComponentModel.EnumConverter class. To
specify the value for an enumerated property, simply use the name of the
property. For example the TestObject class has a
property of the enumerated type FileMode. One of
the values for this enumeration is named Create. The
following XML fragment shows how to configure this property
<object id="rod" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="name" value="Rod"/> <property name="FileMode" value="Create"/> </object>
Spring.NET pre-registers a number of custom
TypeConverter instances (for example, to convert
a type expressed as a string into a real
System.Type object). Each of those is listed
below and they are all located in the
Spring.Objects.TypeConverters namespace of the
Spring.Core library.
Table 4.5. Built-in TypeConverters
| Type | Explanation |
|---|---|
RuntimeTypeConverter | Parses strings representing
System.Types to actual
System.Types and the other way
around. |
FileInfoConverter | Capable of resolving strings to a
System.IO.FileInfo object. |
StringArrayConverter | Capable of resolving a comma-delimited list of strings to a string-array and vice versa. |
UriConverter | Capable of resolving a string representation of a URI
to an actual URI-object. |
CredentialConverter | Capable of resolving a string representation of a
credential for Web client authentication into an instance
ofSystem.Net.ICredentials |
StreamConverter | Capable of resolving Spring IResource URI (string) to
its corresponding
InputStream-object. |
ResourceConverter | Capable of resolving Spring IResource URI (string) to
an IResource object. |
ResourceManagerConverter | Capable of resolving a two part string (resource name,
assembly name) to a
System.Resources.ResourceManager
object. |
RGBColorConverter | Capable of resolving a comma separated list of Red,
Green, Blue integer values to a
System.Drawing.Color structure. |
ExpressionConverter | Capable of resolving a string into an instance of an
object that implements the IExpression
interface. |
NameValueConverter | Capable of resolving an XML formatted string to a
Specialized.NameValueCollection |
RegExConverter | Capable of resolving a string into an instance of
Regex |
RegistryKeyConverter | Capable of resolving a string into a
Microsoft.Win32.RegistryKey object. |
Spring.NET uses the standard .NET mechanisms for the resolution of
System.Types, including, but not limited to
checking any configuration files associated with your application,
checking the Global Assembly Cache (GAC), and assembly probing.
There are a few ways to register custom type converters. The
fundamental storage area in Spring for custom type converters is the
TypeConverterRegistry class. The most
convenient way if using an XML based implementation of
IObjectFactory or
IApplicationContext is to use the the custom
configuration section handler
TypeConverterSectionHandler This is demonstrated
in section Section 4.12, “Configuration of IApplicationContext”
An alternate approach, present for legacy reasons in the port of
Spring.NET from the Java code base, is to use the object factory
post-processor
Spring.Objects.Factory.Config.CustomConverterConfigurer.
This is described in the next section.
If you are constructing your IoC container programmatically then
you should use the RegisterCustomConverter(Type requiredType,
TypeConverter converter) method of the
ConfigurableObjectFactory interface.
This section shows in detail how to define a custom type
converter that does not use the .NET
TypeConverter attribute. The type converter
class is standalone and inherits from the
TypeConverter class. It uses the legacy factory
post-processor approach.
Consider a user class ExoticType, and another class DependsOnExoticType which needs ExoticType set as a property:
public class ExoticType
{
private string name;
public ExoticType(string name)
{
this.name = name;
}
public string Name
{
get { return this.name; }
}
}and
public class DependsOnExoticType
{
public DependsOnExoticType() {}
private ExoticType exoticType;
public ExoticType ExoticType
{
get { return this.exoticType; }
set { this.exoticType = value; }
}
public override string ToString()
{
return exoticType.Name;
}
}When things are properly set up, we want to be able to assign the type property as a string, which a TypeConverter will convert into a real ExoticType object behind the scenes:
<object name="sample" type="SimpleApp.DependsOnExoticType, SimpleApp"> <property name="exoticType" value="aNameForExoticType"/> </object>
The TypeConverter looks like this:
public class ExoticTypeConverter : TypeConverter
{
public ExoticTypeConverter()
{
}
public override bool CanConvertFrom (
ITypeDescriptorContext context,
Type sourceType)
{
if (sourceType == typeof (string))
{
return true;
}
return base.CanConvertFrom (context, sourceType);
}
public override object ConvertFrom (
ITypeDescriptorContext context,
CultureInfo culture, object value)
{
if (value is string)
{
string s = value as string;
return new ExoticType(s.ToUpper());
}
return base.ConvertFrom (context, culture, value);
}
} Finally, we use the
CustomConverterConfigurer to register the new
TypeConverter with the
IApplicationContext, which will then be able to
use it as needed:
<object id="customConverterConfigurer"
type="Spring.Objects.Factory.Config.CustomConverterConfigurer, Spring.Core">
<property name="CustomConverters">
<dictionary>
<entry key="SimpleApp.ExoticType">
<object type="SimpleApp.ExoticTypeConverter"/>
</entry>
</dictionary>
</property>
</object>Spring.NET uses several marker interfaces to change the behaviour
of your object in the container, namely the Spring.NET specific
IInitializingObject interface and the standard
System.IDisposable interfaces. Implementing either of
the aforementioned interfaces will result in the container calling the
AfterPropertiesSet() method for the former and the
Dispose() method for the latter, thus allowing you to
do things upon the initialization and destruction of your
objects.
Internally, Spring.NET uses implementations of the
IObjectPostProcessor interface to process any
marker interfaces it can find and call the appropriate methods. If you
need custom features or other lifecycle behavior Spring.NET doesn't
offer out-of-the-box, you can implement an
IObjectPostProcessor yourself. More information
about this can be found in Section 4.8, “Customizing objects with
IObjectPostProcessors”.
All the different lifecycle marker interfaces are described below.
The
Spring.Objects.Factory.IInitializingObject
interface gives you the ability to perform initialization work after
all the necessary properties on an object are set by the container.
The IInitializingObject interface specifies
exactly one method:
void AfterPropertiesSet(): called
after all properties have been set by the container. This method
enables you to do checking to see if all necessary properties
have been set correctly, or to perform further initialization
work. You can throw any
Exception to indicate misconfiguration,
initialization failures, etc.
![]() | Note |
|---|---|
Generally, the use of the
IInitializingObject
can be avoided. The
Spring.Core
library provides support for a generic init-method, given to the object definition in the object configuration store (be it XML, or a database, etc).
|
<object id="exampleInitObject" type="Examples.ExampleObject" init-method="init"/>
[C#]
public class ExampleObject
{
public void Init()
{
// do some initialization work
}
}Is exactly the same as...
<object id="exampleInitObject" type="Examples.AnotherExampleObject"/>
[C#]
public class AnotherExampleObject : IInitializingObject
{
public void AfterPropertiesSet()
{
// do some initialization work
}
}but does not couple the code to Spring.NET.
![]() | Note |
|---|---|
When deploying an object in
prototype
mode, the lifecycle of the object changes slightly. By definition, Spring.NET cannot manage the complete lifecycle of a non-singleton /
prototype
object, since after it is created, it is given to the client and the container no longer keeps a reference to the object. You can think of Spring.NET's role when talking about a non-singleton (
prototype
) object as a replacement for the
new
operator. Any lifecycle aspects past that point have to be handled by the client.
|
The System.IDisposable interface provides
you with the ability to get a callback when an
IObjectFactory is destroyed. The
IDisposable interface specifies exactly one
method:
void Dispose(): and is called on
destruction of the container. This allows you to release any
resources you are keeping in this object (such as database
connections). You can throw any
Exception here... however, any such
Exception will not stop the destruction
of the container - it will only get logged.
![]() | Note |
|---|---|
Note: If you choose you can avoid having your class
implement IDisposable since the
Spring.Core library provides support for a
generic destroy-method, given to the object definition in the object
configuration store (be it XML, or a database, etc). |
<object id="exampleInitObject" type="Examples.ExampleObject" destroy-method="cleanup"/>
[C#]
public class ExampleObject
{
public void cleanup()
{
// do some destruction work (such as closing any open connection (s))
}
}is exactly the same as:
<object id="exampleInitObject" type="Examples.AnotherExampleObject"/>
[C#]
public class AnotherExampleObject : IDisposable
{
public void Dispose()
{
// do some destruction work
}
}A class which implements the
Spring.Objects.Factory.IObjectFactoryAware
interface is provided with a reference to the
IObjectFactory that created it. The interface
specifies one (write-only) property:
IObjectFactory ObjectFactory: the
property that will be set after the initialization
methods (AfterPropertiesSet and
the init-method).
This allows objects to manipulate the
IObjectFactory that created them
programmatically, through the IObjectFactory
interface, or by casting the reference to a known subclass of this
which exposes additional functionality. Primarily this would consist
of programmatic retrieval of other objects. While there are cases when
this capability is useful, it should generally be avoided, since it
couples the code to Spring.NET, and does not follow the Inversion of
Control style, where collaborators are provided to objects as
properties.
The
Spring.Objects.Factory.IObjectNameAware
interface gives you the ability to let the container set the name of
the object definition on the object instance itself. In those cases
where your object needs to know what its name is, implement this
interface.
string ObjectName: the property that
will be set to let the object know what its name is.
The Spring.Objects.Factory.IFactoryObject
interface is to be implemented by objects that are themselves
factories. The IFactoryObject
interface provides one method and two (read-only) properties:
object GetObject(): has to return an
instance of the object this factory creates. The instance can
possibly be shared (depending on whether this factory provides
singletons or prototypes).
bool IsSingleton: has to return
true if this IFactoryObject returns singletons,
false otherwise.
Type ObjectType: has to return either the
object type returned by the GetObject()
method or null if the type isn't known in
advance.
IFactoryObject implementions you already
have examples of in Section 4.3.5, “Setting a reference using the members of other objects and
classes.”
are PropertyRetrievingFactoryObject and
FieldRetrievingFactoryObject.
Finally, there is sometimes a need to ask a container for an
actual IFactoryObject instance itself, not the
object it produces. This may be achieved by prepending the object id
with '&' (sans quotes) when calling the
GetObject method of the
IObjectFactory (including
IApplicationContext). So for a given
IFactoryObject with an id of
'myObject', invoking
GetObject("myObject") on the container will return
the product of the IFactoryObject, but invoking
GetObject("&myObject") will return the
IFactoryObject instance itself.
The
Spring.Objects.Factory.IConfigurableFactoryObject
interface inherits from IFactoryObject interface
and adds the following property.
IObjectDefinition ProductTemplate : Gets
the template object definition that should be used to configure
the instance of the object managed by this factory.
IConfigurableFactoryObject implementions
you already have examples of in Section 24.2, “Client-side” are
WebServiceProxyFactory.
An object definition potentially contains a large amount of configuration information, including container specific information (i.e. initialization method, static factory method name, etc.) and constructor arguments and property values. A child object definition is an object definition that inherits configuration data from a parent definition. It is then able to override some values, or add others, as needed. Using parent and child object definitions can potentially save a lot of typing. Effectively, this is a form of templating.
When working with an IObjectFactory
programmatically, child object definitions are represented by the
ChildObjectDefinition class. Most users will never
work with them on this level, instead configuring object definitions
declaratively in something like the
XmlObjectFactory. In an
XmlObjectFactory object definition, a child object
definition is indicated simply by using the parent attribute, specifying
the parent object definition as the value of this attribute.
<object id="inheritedTestObject" type="Spring.Objects.TestObject, Spring.Core.Tests"> <property name="name" value="parent"/> <property name="age" value="1"/> </object> <object id="inheritsWithDifferentClass" type="Spring.Objects.DerivedTestObject, Spring.Core.Tests" parent="inheritedTestObject" init-method="Initialize"> <property name="name" value="override"/> <!-- age will inherit value of 1 from parent --> </object>
A child object definition will use the object class from the parent definition if none is specified, but can also override it. In the latter case, the child object class must be compatible with the parent, i.e. it must accept the parent's property values.
A child object definition will inherit constructor argument values, property values and method overrides from the parent, with the option to add new values. If init method, destroy method and/or static factory method are specified, they will override the corresponding parent settings.
The remaining settings will always be taken from the child
definition: depends on, autowire
mode, dependency check,
singleton, lazy init.
In the case where the parent definition does not specify a class...
<object id="inheritedTestObjectWithoutClass"> <property name="name" value="parent"/> <property name="age" value="1"/> </object> <object id="inheritsWithClass" type="Spring.Objects.DerivedTestObject, Spring.Core.Tests" parent="inheritedTestObjectWithoutClass" init-method="Initialize"> <property name="name" value="override"/> <!-- age will inherit value of 1 from parent --> </object>
... the parent object cannot be instantiated on its own since the
definition is incomplete. The definition is also implicitly considered to
be abstract. An object definition can also be explicitly declared as
abstract using the abstract attribute. Valid values of the attribute are
true and false. An abstract definition like this is usable just as a pure
template or abstract object definition that will serve as a parent
definition for child definitions. Trying to use such parent objects on its
own (by referring to it as a ref property of another object, or doing an
explicit GetObject() with the parent object id),
will result in an error. Declaring the object as abstract will prevent it
being instantiated and any attempt to instantiate the object will result
in an ObjectDefinitionIsAbstract exception being
thrown. The container's internal
PreInstantiateSingletons method will completely
ignore object definitions which are considered abstract.
![]() | Note |
|---|---|
| Application contexts (but not simple object factories) will by default pre-instantiate all singletons. Therefore it is important (at least for singleton objects) that if you have a (parent) object definition which you intend to use only as a template, and this definition specifies a class, you must make sure to set the abstract attribute to true , otherwise the application context will actually attempt to pre-instantiate it. |
<object id="abstractObject" abstract="true" type="Spring.Objects.DerivedTestObject, Spring.Core.Tests"> <property name="name" value="parent"/> <property name="age" value="1"/> </object> <object id="inheritsFromAbstract" type="Spring.Objects.DerivedTestObject, Spring.Core.Tests" parent="inheritedTestObjectWithoutClass" init-method="Initialize"> <property name="name" value="override"/> <!-- age will inherit value of 1 from parent --> </object>
The IObjectFactory is essentially nothing
more than an advanced factory capable of maintaining a registry of
different objects and their dependencies. The
IObjectFactory enables you to read object
definitions and access them using the object factory. When using just the
IObjectFactory you would create an instance of one
and then read in some object definitions in the XML format as follows:
[C#]
IResource input = new FileSystemResource ("objects.xml");
XmlObjectFactory factory = new XmlObjectFactory(input);That is pretty much it. Using GetObject(string)
(or the more concise indexer method factory ["string"])
you can retrieve instances of your objects...
[C#]
object foo = factory.GetObject ("foo"); // gets the object defined as 'foo'
object bar = factory ["bar"]; // same thing, just using the indexer
You'll get a reference to the same object if you defined it as a
singleton (the default) or you'll get a new instance each time if you set
the singleton property of your object definition to
false.
<object id="exampleObject" type="Examples.ExampleObject, ExamplesLibrary"/> <object id="anotherObject" type="Examples.ExampleObject, ExamplesLibrary" singleton="false"/>
[C#] object one = factory ["exampleObject"]; // gets the object defined as 'exampleObject' object two = factory ["exampleObject"]; Console.WriteLine (one == two) // prints 'true' object three = factory ["anotherObject"]; // gets the object defined as 'anotherObject' object four = factory ["anotherObject"]; Console.WriteLine (three == four); // prints 'false'
The client-side view of the IObjectFactory is
surprisingly simple. The IObjectFactory interface
has only seven methods (and the aforementioned indexer) for clients to
call:
bool ContainsObject(string): returns true
if the IObjectFactory contains an object
definition that matches the given name.
object GetObject(string): returns an
instance of the object registered under the given name. Depending on
how the object was configured by the
IObjectFactory configuration, either a
singleton (and thus shared) instance or a newly created object will
be returned. An ObjectsException will be thrown
when either the object could not be found (in which case it'll be a
NoSuchObjectDefinitionException), or an exception
occurred while instantiated and preparing the object.
Object this [string]: this is the indexer
for the IObjectFactory interface. It
functions in all other respects in exactly the same way as the
GetObject(string) method. The rest of this
documentation will always refer to the
GetObject(string) method, but be aware that you
can use the indexer anywhere that you can use the
GetObject(string) method.
Object GetObject(string, Type): returns an
object, registered under the given name. The object returned will be
cast to the given Type. If the object could
not be cast, corresponding exceptions will be thrown
(ObjectNotOfRequiredTypeException). Furthermore,
all rules of the GetObject(string) method apply
(see above).
bool IsSingleton(string): determines
whether or not the object definition registered under the given name
is a singleton or a prototype. If the object definition
corresponding to the given name could not be found, an exception
will be thrown
(NoSuchObjectDefinitionException)
string[] GetAliases(string): returns the
aliases for the given object name, if any were defined in the
IObjectDefinition.
void ConfigureObject(object target):
Injects dependencies into the supplied target instance. The name of
the abstract object definition is the
System.Type.FullName of the target instance. This
method is typically used when objects are instantiated outside the
control of a developer, for example when ASP.NET instantiates web
controls and when a WinForm application creates UserControls.
void ConfigureObject(object target, string
name): Offers the same functionality as the previously
listed Configure method but uses a named object definition instead
of using the type's full name.
Sometimes there is a need to ask an
IObjectFactory for an actual
IFactoryObject instance itself, not the object it
produces. This may be done by prepending the object id with
& when calling the
GetObject method of the
IObjectFactory and
IApplicationContext interfaces. So for a given
IFactoryObject with an id
myObject, invoking
GetObject("myObject") on the
IObjectFactory will return the product of the
IFactoryObject, but invoking
GetObject("&myObject") will return the
IFactoryObject instance itself.
An object post-processor is a class that implements the
Spring.Objects.Factory.Config.IObjectPostProcessor
interface, which consists of two callback methods shown below.
object PostProcessBeforeInitialization(object instance, string name); object PostProcessAfterInitialization(object instance, string name);
When such a class is registered as a post-processor with the container,
for each object instance that is created by the container, the
post-processor method
PostProcessBeforeInitialization will be called by
the container before any initialization methods such as the
AfterPropertiesSet method of the
IInitializingObject interface and any declared init
method) are called. The post-processor method
PostProcessAfterInitialization is called
subsequent to any init methods.
The post-processor is free to do what it wishes with the object, including ignoring the callback completely. An object post-processor will typically check for marker interfaces, or do something such as wrap an object with a proxy. Some Spring.NET helper classes are implemented as object post-processors.
Other extensions to the the IObjectPostProcessors
interface are
IInstantiationAwareObjectPostProcessor and
IDestructionAwareObjectPostProcessor defined below
public interface IInstantiationAwareObjectPostProcessor : IObjectPostProcessor
{
object PostProcessBeforeInstantiation(Type objectType, string objectName);
}
public interface IDestructionAwareObjectPostProcessor : IObjectPostProcessor
{
void PostProcessBeforeDestruction (object instance, string name);
} The "BeforeInstantiation" callback method is called right
before the container creates the object. If the object returned by this
method is not null then the default instantiation behavor of the container
is short circuited. The returned object is the one registered with the
container and no other IObjectPostProcessor callbacks
will be invoked on it. This mechanism is useful if you would like to
expose a proxy to the object instead of the actual target object. The
"BeforeDestruction" callack is called before a singletons destroy method
is invoked.
It is important to know that the
IObjectFactory treats object post-processors
slightly differently than the IApplicationContext.
An IApplicationContext will automatically detect
any objects which are deployed into it that implement the
IObjectPostProcessor interface, and register them
as post-processors, to be then called appropriately by the factory on
object creation. Nothing else needs to be done other than deploying the
post-processor in a similar fashion to any other object. On the other
hand, when using plain IObjectFactories, object
post-processors have to manually be explicitly registered, with a code
sequence such as...
ConfigurableObjectFactory factory = new .....; // create an IObjectFactory ... // now register some objects // now register any needed IObjectPostProcessors MyObjectPostProcessor pp = new MyObjectPostProcessor(); factory.AddObjectPostProcessor(pp); // now start using the factory ...
Since this manual registration step is not convenient, and
IApplicationContexts are functionally supersets of
IObjectFactories, it is generally recommended that
IApplicationContext variants are used when object
post-processors are needed.
An object factory post-processor is a class that implements the
Spring.Objects.Factory.Config.IObjectFactoryPostProcessor
interface. It is executed manually (in the case of the
IObjectFactory) or automatically (in the case of
the IApplicationContext) to apply changes of some
sort to an entire ObjectFactory, after it has been constructed. By
implementing this interface, you will receive a callback after the all the
object definitions have been loaded into the IoC container but before they
have been instantiated. This gives you the opportunity to modify the
configuration of any object in the IoC container or perform initialization
before objects are constructed. The signature of the interface is shown
below
public interface IObjectFactoryPostProcessor
{
void PostProcessObjectFactory (IConfigurableListableObjectFactory factory);
}
Generally speaking, if you are looking to interact with the
lifecycle of the objects created in the IoC container, consult the
functionality provided by the
Spring.Objects.Factory.Config namespace, which should
provide you all the things you need. Using the types defined in the
Spring.Objects.Factory.Config namespace you can for
instance define a properties file of which the property-entries are used
as replacements values in the IObjectFactory. You
are free to define you own implementations of the
IObjectFactoryPostProcessor interface. Spring.NET
includes a number of pre-existing object factory post-processors, such as
PropertyResourceConfigurer and
PropertyPlaceHolderConfigurer, both described
below.
In the case of an IObjectFactory, the process
of applying an IObjectFactoryPostProcessor is
manual, and will be similar to this:
XmlObjectFactory factory = new XmlObjectFactory(new FileSystemResource("objects.xml"));
// create placeholderconfigurer to bring in some property
// values from a Properties file
PropertyPlaceholderConfigurer cfg = new PropertyPlaceholderConfigurer();
cfg.setLocation(new FileSystemResource("ado.properties"));
// now actually do the replacement
cfg.PostProcessObjectFactory(factory);An
IApplicationContext will detect any objects which
are deployed into it which implement the
ObjectFactoryPostProcessor interface, and automatically
use them as object factory post-processors, at the appropriate time.
Nothing else needs to be done other than deploying these post-processor in
a similar fashion to any other object.
Since this manual step is not convenient, and
IApplicationContexts are functionally supersets of
ObjectFactories, it is generally recommended that
IApplicationContext variants are used when object
factory post-processors are needed.
The PropertyPlaceholderConfigurer is an
excellent solution when you want to externalize a few properties from a
file containing object definitions. Say you want to let the person
responsible for deploying applications just fill in database URLs,
usernames and passwords. You could of course let him or her change the
XML file containing object definitions, but that would be risky.
Instead, use the PropertyPlaceHolderConfigurer to
- at runtime - replace those properties by values from a configuration
file.
Variable substitution is performed on simple property values, lists, dictionaries, sets, constructor values, object type name, and object names in runtime object references. Furthermore, placeholder values can also cross-reference other placeholders.
Note that IApplicationContexts are able to
automatically recognize and apply objects deployed in them that
implement the IObjectFactoryPostProcessor
interface. This means that as described here, applying a
PropertyPlaceholderConfigurer is much more
convenient when using an IApplicationContext. For
this reason, it is recommended that users wishing to use this or other
object factory postprocessors use an
IApplicationContext instead of an
IObjectFactory.
In the example below a data access object needs to be configured with a database connection and also a value for the maximum number of results to return in a query. Instead of hard coding the values into the main Spring.NET configuration file we use place holders, in the NAnt style of ${variableName}, and obtain their values from NameValueSections in the standard .NET application configuration file. The Spring.NET configuration file looks like:
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
</sectionGroup>
<section name="DaoConfiguration" type="System.Configuration.NameValueSectionHandler"/>
<section name="DatabaseConfiguration" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<DaoConfiguration>
<add key="maxResults" value="1000"/>
</DaoConfiguration>
<DatabaseConfiguration>
<add key="connection.string" value="dsn=MyDSN;uid=sa;pwd=myPassword;"/>
</DatabaseConfiguration>
<spring>
<context>
<resource uri="assembly://DaoApp/DaoApp/objects.xml"/>
</context>
</spring>
</configuration>Notice the presence of two NameValueSections in the configuration file. These name value pairs will be referred to in the Spring.NET configuration file. In this example we are using an embedded assembly resource for the location of the Spring.NET configuration file so as to reduce the chance of accidental tampering in deployment. This Spring.NET configuration file is shown below.
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net
http://www.springframework.net/xsd/spring-objects.xsd" >
<object name="productDao" type="DaoApp.SimpleProductDao, DaoApp ">
<property name="maxResults" value="${maxResults}"/>
<property name="dbConnection" ref="myConnection"/>
</object>
<object name="myConnection" type="System.Data.Odbc.OdbcConnection, System.Data">
<property name="connectionstring" value="${connection.string}"/>
</object>
<object name="appConfigPropertyHolder"
type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
<property name="configSections">
<value>DaoConfiguration,DatabaseConfiguration</value>
</property>
</object>
</objects>The values of ${maxResults} and
${connection.string} match the key names used in the
two NameValueSectionHandlers DaoConfiguration and
DatabaseConfiguration. The
PropertyPlaceholderConfigurer refers to these two
sections via a comma delimited list of section names in the
configSections property.
The PropertyPlaceholderConfigurer class
also supports retrieving name value pairs from other
IResource locations. These can be specified using
the Location and Locations
properties of the PropertyPlaceHolderConfigurer
class.
If there are properties with the same name in different resource
locations the default behavior is that the last property processed
overrides the previous values. This is behavior is controlled by the
LastLocationOverrides property. True enables
overriding while false will append the values as one would normally
expect using NameValueCollection.Add.
![]() | Note |
|---|---|
In an ASP.NET environment you must specify the full, four-part name of the assembly when using a
NameValueFileSectionHandler
; to wit...
|
<section name="hibernateConfiguration"
type="System.Configuration.NameValueFileSectionHandler, System,
Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>You may also use the value environment variables to replace
property placeholders. The use of environment variables is controlled
via the property EnvironmentVariableMode. This
property is an enumeration of the type
EnvironmentVariablesMode and has three values,
Never, Fallback, and Override. Fallback is the
default value and will resolve a property placeholder if it was not
already done so via a value from a resource location.
Override will apply environment variables before
applying values defined from a resource location.
Never will, quite appropriately, disable
environment variable substitution. An example of how the
PropertyPlaceholderConfigurerXML is modified to
enable override usage is shown below
<object name="appConfigPropertyHolder"
type="Spring.Objects.Factory.Config.PropertyPlaceholderConfigurer, Spring.Core">
<property name="configSections" value="DaoConfiguration,DatabaseConfiguration"/>
<property name="EnvironmentVariableMode" value="Override"/>
</object>
</objects>The PropertyOverrideConfigurer, another
object factory post-processor, is similar to the
PropertyPlaceholderConfigurer, but in contrast to
the latter, the original definitions can have default values or no
values at all for object properties. If an overriding configuration file
file does not have an entry for a certain object property, the default
context definition is used.
Note that the object factory definition is
not aware of being overridden, so it is not
immediately obvious when looking at the XML definition file that the
override configurer is being used. In case that there are multiple
PropertyOverrideConfigurer instances that define
different values for the same object property, the last one will win
(due to the overriding mechanism).
The example usage is similar to when using
PropertyPlaceHolderConfigurer except that the key
name refers to the name given to the object in the Spring.NET
configuration file and is suffixed via 'dot' notation with the name of
the property For example, if the application configuration file is
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core"/>
</sectionGroup>
<section name="DaoConfigurationOverride" type="System.Configuration.NameValueSectionHandler"/>
</configSections>
<DaoConfigurationOverride>
<add key="productDao.maxResults" value="1000"/>
</DaoConfigurationOverride>
<spring>
<context>
<resource uri="assembly://DaoApp/DaoApp/objects.xml"/>
</context>
</spring>
</configuration>Then the value of 1000 will be used to overlay the value of 2000 set in the Spring.NET configuration file shown below
<objects xmlns="http://www.springframework.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.net http://www.springframework.net/xsd/spring-objects.xsd" >
<object name="productDao" type="PropPlayApp.SimpleProductDao, PropPlayApp " >
<property name="maxResults" value="2000"/>
<property name="dbConnection" ref="myConnection"/>
<property name="log" ref="daoLog"/>
</object>
<object name="daoLog" type="Spring.Objects.Factory.Config.Log4NetFactoryObject, Spring.Core">
<property name="logName" value="DAOLogger"/>
</object>
<object name="myConnection" type="System.Data.Odbc.OdbcConnection, System.Data">
<property name="connectionstring">
<value>dsn=MyDSN;uid=sa;pwd=myPassword;</value>
</property>
</object>
<object name="appConfigPropertyOverride" type="Spring.Objects.Factory.Config.PropertyOverrideConfigurer, Spring.Core">
<property name="configSections">
<value>DaoConfigurationOverride</value>
</property>
</object>
</objects>The IVariableSource is the base interface for providing the ability to get the value of property placeholders (name-value) pairs from a variety of sources. Out of the box, Spring.NET supports a number of variable sources that allow users to obtain variable values from .NET config files, java-style property files, environment variables, command line arguments and the registry and the new connection strings configuraiton section in .NET 2.0. The list of implementing classes is listed below. Please refer to the SDK documentation for more information.
ConfigSectionVariableSource
PropertyFileVariableSource
EnvironmentVariableSource
CommandLineArgsVariableSource
RegistryVariableSource
SpecialFolderVariableSource
ConnectionStringsVariableSource
You use this by defining an instance of
Spring.Objects.Factory.Config.VariableVariablePlaceholderConfigurer
in your configuraiton and set the property
VariableSource to a single
IVariableSource instance or the list property
VariableSources to a list of IVariableSource
instances. In the case of the same property defined in multiple
IVariableSource implementations, the first one in
the list that contains the property value will be used.
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 Section 4.2.5, “The object identifiers (id and
name)”. This approach to
aliasing objects has some limitations when you would like to assemble the
main application configuration file from multiple files. See Section 4.15, “Importing Object Definitions from One File Into
Another” 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
mechansim. 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 achive 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 fragement 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.
While the Spring.Objects namespace provides basic
functionality for managing and manipulating objects, often in a
programmatic way, the Spring.Context namespace
introduces the IApplicationContext interface, which
enhances the functionality provided by the
IObjectFactory interface.
The basis for the context module is the
IApplicationContext interface, located in the
Spring.Context namespace. To be able to work in a more
framework-oriented way, using layering and hierarchical contexts, the
Spring.Context namespace also provides the following:
Loading of multiple (hierarchical) contexts, allowing some of them to be focused and used on one particular layer, for example the web layer of an application.
Access to localized resources at the
application level by implementing
IMessageSource.
Uniform access to resources that can be
read in as an InputStream, such as URLs and files by implementing
IResourceLoader
Loosely Coupled Event Propagation. Publishers and subscribers of events do not have to be directly aware of each other as they register their interest indirectly through the application context.
As the IApplicationContext includes all the
functionality the object factory via its inheritance of the
IObjectFactory interface, it is generally
recommended to be used over the IObjectFactory.
Classes that implement only the IObjectFactory
interface, such as XmlObjectFactory, reflect the
initial creation of the framework in Java to support resource constrained
environments.
Since the IApplicationContext is a subclass
of IObjectFactory, you can define objects using the
XML format (as explained in Section 4.2.1, “The IObjectFactory and IApplicationContext” and Appendix A, Spring.NET's spring-objects.xsd ) and retrieve them using their unique
identifier. Also, you will be able to use all of the other features
explained in the previous chapters, such as
autowiring, setter-based or
constructor-based dependency injection,
dependency checking, lifecycle
interfaces, etc.
The features unique to the
IApplicationContext are described below. These are
Singleton service locator style access. See Section 4.17, “Service Locator access”
Automatic registration of objects that implement the
IObjectPostProcessors interface. See Section 4.14, “Customized behavior in the ApplicationContext”
Registration of classes used internally by
IApplicationContext such as resource protocol
handlers, XML parsers for object definitions, and type aliases. See
Section 4.12, “Configuration of IApplicationContext”
Well known locations in the .NET application configuration file are used to register resource handlers, custom parsers, type alias, and custom type converts in addition to the context and objects sections mentioned previously. A sample .NET application configuration file showing all these features is shown below. Each section require the use of a custom configuration section handler. Note that the types shown for resource handlers and parsers are fictional.
<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" />
<section name="parsers" type="Spring.Context.Support.ConfigParsersSectionHandler, Spring.Core"/>
<section name="resources" type="Spring.Context.Support.ResourcesSectionHandler, Spring.Core"/>
<section name="typeAliases" type="Spring.Context.Support.TypeAliasesSectionHandler, Spring.Core"/>
<section name="typeConverters" type="Spring.Context.Support.TypeConvertersSectionHandler, Spring.Core"/>
</sectionGroup>
</configSections>
<spring>
<parsers>
<parser type="Spring.Aop.Config.AopNamespaceParser, Spring.Aop" />
<parser type="Spring.Web.Config.WebNamespaceParser, Spring.Web" />
</parsers>
<resources>
<resource protocol="db" type="MyCompany.MyApp.Resources.MyDbResource"/>
</resources>
<context caseSensitive="false">
<resource uri="config://spring/objects"/>
<resource uri="db://user:pass@dbName/MyDefinitionsTable"/>
</context>
<typeAliases>
<alias name="WebServiceExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web"/>
<alias name="DefaultPointcutAdvisor" type="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"/>
<alias name="AttributePointcut" type="Spring.Aop.Support.AttributeMatchMethodPointcut, Spring.Aop"/>
<alias name="CacheAttribute" type="Spring.Attributes.CacheAttribute, Spring.Core"/>
<alias name="MyType" type="MyCompany.MyProject.MyNamespace.MyType, MyAssembly"/>
</typeAliases>
<typeConverters>
<converter for="Spring.Expressions.IExpression, Spring.Core" type="Spring.Objects.TypeConverters.ExpressionConverter, Spring.Core"/>
<converter for="MyTypeAlias" type="MyCompany.MyProject.Converters.MyTypeConverter, MyAssembly"/>
</typeConverters>
<objects xmlns="http://www.springframework.net">
...
</objects>
</spring>
</configuration>
The new sections are described below. The attribute
caseSensitive allows the for both
IObjectFactory and
IApplicationContext implementations to not pay
attention to the case of the object names. This is important in web
applications so that ASP.NET pages can be resolved in a case independent
manner. The default value is true.
Instead of using the default XML schema that is generic in nature
to define an object's properties and dependencies, you can create your
own XML schema specific to an application domain. This has the benefit
of being easier to type and getting XML intellisence for the schema
being used. The downside is that you need to write code that will
transform this XML into Spring object definitions. One would typically
implement a custom parser by deriving from the class
DefaultXmlObjectDefinitionParser and overriding
the methods int ParseRootElement(XmlElement root,
XmlResourceReader reader) and int
ParseElement(XmlElement element, XmlResourceReader reader).
Registering custom parsers outside of App.config will be addressed in a
future release.
Creating a custom resource handler means implementing the
IResource interface. The base class
AbstractResource is a useful starting point. Look
at the Spring source for classes such as
FileSystemResource or
AssemblyResource for implementation tips. You can
register your custom resource handler either within App.config, as shown
above using a .ResourcesSectionHandler or define
an object of the type
Spring.Objects.Factory.Config.ResourceHandlerConfigurer.
An example of the latter is shown below:
<object id="myResourceHandlers" type="Spring.Objects.Factory.Config.ResourceHandlerConfigurer, Spring.Core">
<property name="ResourceHandlers">
<dictionary>
<entry key="db" value="MyCompany.MyApp.Resources.MyDbResource, MyAssembly"/>
</dictionary>
</property>
</object>Type aliases allow you to simplify Spring configuration file by replacing fully qualified type name with an alias for frequently used types. Aliases can be registered both within config file and programatically and can be used anywhere in the context config file where fully qualified type name is expected. Type aliases can also be defined for generic types.
One way to configure a type alias is to define them in a custom config section in the Web/App.config file for your application, as well as the custom configuration section handler. See the previous XML configuration listing for an example that makes an alias for the WebServiceExporter type. Once you have aliases defined, you can simply use them anywhere where you would normally specify fully qualified type name:
<object id="MyWebService" type="WebServiceExporter">
...
</object>
<object id="cacheAspect" type="DefaultPointcutAdvisor">
<property name="Pointcut">
<object type="AttributePointcut">
<property name="Attribute" value="CacheAttribute"/>
</object>
</property>
<property name="Advice" ref="aspNetCacheAdvice"/>
</object>For an example showing type aliases for generic types see Section 4.2.4, “Object creation of generic types”.
Another way is to define an object of the type
Spring.Objects.Factory.Config.TypeAliasConfigurer
within the regular <objects> section of any standard Spring
configuration file. This approach allows for more modularity in defining
type aliases, for example if you can't access App.config/Web.config. An
example of registration using a
TypeAliasConfigurer is shown below
<object id="myTypeAlias" type="Spring.Objects.Factory.Config.TypeAliasConfigurer, Spring.Core">
<property name="TypeAliases">
<dictionary>
<entry key="WebServiceExporter" value="Spring.Web.Services.WebServiceExporter, Spring.Web"/>
<entry key="DefaultPointcutAdvisor" value="Spring.Aop.Support.DefaultPointcutAdvisor, Spring.Aop"/>
<entry key="MyType" value="MyCompany.MyProject.MyNamespace.MyType, MyAssembly"/>
</dictionary>
</property>
</object>The standard .NET mechanism for specifying a type converter is to
add a TypeConverter attribute to type definition
and specify the type of the Converter. This is the preferred way of
defining type converters if you control the source code for the type
that you want to define a converter for. However, this configuration
section allows you to specify converters for the types that you don't
control, and it also allows you to override some of the standard type
converters, such as the ones that are defined for some of the types in
the .NET Base Class Library.
You can specify the type converters in App.config by using
Spring.Context.Support.TypeConvertersSectionHandler as shown before or
define an object of the type
Spring.Objects.Factory.Config.CustomConverterConfigurer.
An example of registration using a
CustomConverterConfigurer is shown below
<object id="myTypeConverters" type="Spring.Objects.Factory.Config.CustomConverterConfigurer, Spring.Core">
<property name="CustomConverters">
<dictionary>
<entry key="System.Date" value="MyCompany.MyProject.MyNamespace.MyCustomDateConverter, MyAssembly"/>
</dictionary>
</property>
</object>As already stated in the previous section, the
IApplicationContext has a couple of features that
distinguish it from the IObjectFactory. Let us
review them one-by-one.
You can structure the configuration information of application context into hierarchies that naturally reflect the internal layering of your application. As an example, abstract object definitions may appear in a parent application context configuration file, possibly as an embedded assembly resource so as not to invite accidental changes.
<spring>
<context>
<resource uri="assembly://MyAssembly/MyProject/root-objects.xml"/>
<context name="mySubContext">
<resource uri="file://objects.xml"/>
</context>
</context>
</spring> The nesting of context
elements reflects the parent-child hierarchy you are creating. The
nesting can be to any level though it is unlikely one would need a deep
application hierarchy. The xml file must contain the
<objects> as the root name. Another example of
a hierarchy, but using sections in the application configuration file is
shown below.
<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 name="child">
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</sectionGroup>
</configSections>
<spring>
<context name="ParentContext">
<resource uri="config://spring/objects"/>
<context name="ChildContext">
<resource uri="config://spring/child/objects"/>
</context>
</context>
<objects xmlns="http://www.springframework.net">
...
</objects>
<child>
<objects xmlns="http://www.springframework.net">
...
</objects>
</child>
</spring>As a reminder, the type attribute of the
context tag is optional and defaults to
Spring.Context.Support.XmlApplicationContext. The
name of the context can be used in conjunction with the service locator
class, ContextRegistry, discussed in Section 4.17, “Service Locator access”
The IApplicationContext interface extends
an interface called IMessageSource and provides
localization (i18n or internationalization) services for text messages
and other resource data types such as images. This functionality makes
it easier to use .NET's localization features at an application level
and also offers some performance enhancements due to caching of
retrieved resources. Together with the
NestingMessageSource, capable of hierarchical message
resolving, these are the basic interfaces Spring.NET provides to for
localization. Let's quickly review the methods defined there:
string GetMessage(string name): retrieves
a message from the IMessageSource and uses
the CurrentUICulture.
string GetMessage(string name, CultureInfo
cultureInfo): retrieves a message from the
IMessageSource and using a specified
culture.
string GetMessage(string name, params object[]
args): retrieves a message from the
IMessageSource using a variable list of
arguments as replacement values in the message. The
CurrentUICulture is used to resolve the message.
string GetMessage(string name, CultureInfo
cultureInfo, params object[] args): retrieves a message
from the IMessageSource using a variable
list of arguments as replacement values in the message. The
specified culture is used to resolve the message.
string GetMessage(string name, string
defaultMessage, CultureInfo culture, params object[]
arguments): retrieves a message from the
IMessageSource using a variable list of
arguments as replacement values in the message. The specified
culture is used to resolve the message. If no message can be
resolved, the default message is used.
string GetMessage(IMessageSourceResolvable
resolvable, : all properties used in the methods above are also
wrapped in a class - the
CultureInfo culture)
MessageSourceResolvable, which you can use in
this method.
object GetResourceObject(string name):Get
a localized resource object, i.e. Icon, Image, etc. given the
resource name. The CurrentUICulture is used to resolve the
resource object.
object GetResourceObject(string name, CultureInfo
cultureInfo):Get a localized resource object, i.e. Icon,
Image, etc. given the resource name. The specified culture is used
to resolve the resource object.
void ApplyResources(object value, string
objectName, CultureInfo cultureInfo): Uses a
ComponentResourceManager to apply resources to all object
properties that have a matching key name. Resource key names are
of the form objectName.propertyName
When an IApplicationContext gets loaded, it
automatically searches for an IMessageSource
object defined in the context. The object has to have the name
messageSource. If such an object is found, all calls
to the methods described above will be delegated to the message source
that was found. If no message source was found, the
IApplicationContext checks to see if it has a
parent containing a similar object, with a similar name. If so, it uses
that object as the IMessageSource. If it can't
find any source for messages, an empty
StaticMessageSource will be instantiated in order to
be able to accept calls to the methods defined above.
![]() | Fallback behavior |
|---|---|
| The fallback rules for localized resources seem to have a bug that is fixed by applying Service Pack 1 for .NET 1.1. This affects the use of IMessageSource.GetMessage methods that specify CultureInfo. The core of the issue in the .NET BCL is the method ResourceManager.GetObject that accepts CultureInfo. . |
Spring.NET provides two IMessageSource
implementations. These are
ResourceSetMessageSource and
StaticMessageSource. Both implement
IHierarchicalMessageSource to resolve messages
hierarchically. The StaticMessageSource is hardly
ever used but provides programmatic ways to add messages to the source.
The ResourceSetMessageSource is more interesting
and an example is provided for in the distribution and discussed more
extensively in the Chapter 27, IoC Quickstarts section. The
ResourceSetMessageSource is configured by
providing a list of ResourceManagers. When a
message code is to be resolved, the list of ResourceManagers is searched
to resolve the code. For each ResourceManager a
ResourceSet is retrieved and asked to resolve the
code. Note that this search does not replace the standard hub-and-spoke
search for localized resources. The ResourceManagers list specifies the
multiple 'hubs' where the standard search starts.
<object name="messageSource" type="Spring.Context.Support.ResourceSetMessageSource, Spring.Core">
<property name="resourceManagers">
<list>
<value>Spring.Examples.AppContext.MyResource, Spring.Examples.AppContext</value>
</list>
</property>
</object>You can specify the arguments to construct a ResourceManager as a
two part string value containing the base name of the resource and the
assembly name. This will be converted to a ResourceManager via the
ResourceManagerConverter TypeConverter. This
converter can be similarly used to set a property on any object that is
of the type ResourceManager. You may also specify
an instance of the ResourceManager to use with
via an object reference. The convenience class
Spring.Objects.Factory.Config.ResourceManagerFactoryObject
can be used to conveniently create an instance of a ResourceManager.
<object name="myResourceManager" type="Spring.Objects.Factory.Config.ResourceManagerFactoryObject, Spring.Core">
<property name="baseName">
<value>Spring.Examples.AppContext.MyResource</value>
</property>
<property name="assemblyName">
<value>Spring.Examples.AppContext</value>
</property>
</object>In application code, a call to GetMessage will
retrieve a properly localized message string based on a code value. Any
arguments present in the retrieved string are replaced using
String.Format semantics. The ResourceManagers,
ResourceSets and retrieved strings are cached to provide quicker lookup
performance. The key 'HelloMessage' is contained in the resource file
with a value of Hello {0} {1}. The following call on
the application context will return the string Hello Mr.
Anderson. Note that the caching of
ResourceSets is via the concatination of ResourceManager base name and
CultureInfo string. This combination must be unique.
string msg = ctx.GetMessage("HelloMessage",
new object[] {"Mr.", "Anderson"},
CultureInfo.CurrentCulture );
It is possible to chain the resolution of messages by passing
arguments that are themselves messages to be resolved giving you greater
flexibility in how you can structure you message resolution. This is
achieved by passing as an argument a class that implements
IMessageResolvable instead of a string literal. The
convenience class DefaultMessageResolvable is
available for this purpose. As an example if the resource file contains
a key name error.required that has the value
'{0} is required {1}' and another key name
field.firstname with the value 'First
name'. The following code will create the string
'First name is required dude!'
string[] codes = {"field.firstname"};
DefaultMessageResolvable dmr = new DefaultMessageResolvable(codes, null);
ctx.GetMessage("error.required",
new object[] { dmr, "dude!" },
CultureInfo.CurrentCulture ));
The examples directory in the distribution contains an example
program, Spring.Examples.AppContext, that
demonstrates the usage of these features.
A lot of applications need to access resources. Resources here,
might mean files, but also news feeds from the Internet or normal web
pages. Spring.NET provides a clean and transparent way of accessing
resources in a protocol independent way. The
IApplicationContext has a method
(GetResource(string)) to take care of this. Refer to
Section 6.1, “Introduction” for more information on the string
format to use and the IResource abstraction in
general.
The Eventing Registry allows developers to utilize a loosely coupled event wiring mechanism. By decoupling the event publication and the event subscription, most of the mundane event wiring is handled by the IoC container. Event publishers can publish their event to a central registry, either all of their events or a subset based on criteria such as delegate type, name, return value, etc... Event subscribers can choose to subscribe to any number of published events. Subscribers can subscriber to events based on the type of object exposing them, allowing one subscriber to handle all events of a certain type without regards to how many different instances of that type are created. Other subscription criteria include name, delegate type, and regular expression matching.
The Spring.Objects.Events.IEventRegistry
interface represents the central registry and defines publish and
subscribe methods.
void PublishEvents( object sourceObject
): publishes all events of the source object to
subscribers that implement the correct handler methods.
void Subscribe(object subscriber ): The
subscriber receives all events from the source object for which it
has matching handler methods.
void Subscribe(object subscriber, Type
targetSourceType ): The subscriber receives all events
from a source object of a particular type for which it has
matching handler methods.
IApplicationContext implements
this interface and delegates the implementation to an instance of
Spring.Objects.Events.Support.EventRegistry. You
are free to create and use as many EventRegistries as you like but since
it is common to use only one in an application,
IApplicationContext provides convenient access to
a single instance.
Within the
example/Spring/Spring.Examples.EventRegistry
directory you will find an example on how to use this functionality.
When you open up the project, the most interesting file is the
EventRegistryApp.cs file. This application loads a set of object
definitions from the application configuration file into an
IApplicationContext instance. From there, three
objects are loaded up: one publisher and two subscribers. The publisher
publishes its events to the IApplicationContext
instance:
// Create the Application context using configuration file
IApplicationContext ctx = ContextRegistry.GetContext();
// Gets the publisher from the application context
MyEventPublisher publisher = (MyEventPublisher)ctx.GetObject("MyEventPublisher");
// Publishes events to the context.
ctx.PublishEvents( publisher );
One of the two subscribers subscribes to all events
published to the IApplicationContext instance,
using the publisher type as the filter criteria.
// Gets first instance of subscriber
MyEventSubscriber subscriber = (MyEventSubscriber)ctx.GetObject("MyEventSubscriber");
// Gets second instance of subscriber
MyEventSubscriber subscriber2 = (MyEventSubscriber)ctx.GetObject("MyEventSubscriber");
// Subscribes the first instance to the any events published by the type MyEventPublisher
ctx.Subscribe( subscriber, typeof(MyEventPublisher) ); This
will wire the first subscriber to the original event publisher. Anytime
the event publisher fires an event,
(publisher.ClientMethodThatTriggersEvent1();) the
first subscriber will handle the event, but the second subscriber will
not. This allows for selective subscription, regardless of the original
prototype definition.
Event handling in the IApplicationContext
is provided through the IApplicationListener
interface that contains the single method void
OnApplicationEvent( object source, ApplicationEventArgs
applicationEventArgs ). Classes that implement the
IApplicationListener interface are automatically
registered as a listener with the
IApplicationContext. Publishing an event is done
via the context's PublishEvent( ApplicationEventArgs eventArgs
) method. This implemenation is based on the traditional
Observer design pattern.
The event argument type,
ApplicationEventArgs, adds the time of the event
firing as a property. The derived class
ContextEventArgs is used to notify observers on
the lifecycle events of the application context. It contains a property
ContextEvent Event that returns the enumeration
Refreshed or Closed.. The
Refreshed enumeration value indicated that the
IApplicationContext was either initialized or
refreshed. Initialized here means that all objects are loaded,
singletons are pre-instantiated and the
IApplicationContext is ready for use. The
Closed is published when the
IApplicationContext is closed using the
Dispose() method on the
IConfigurableApplicationContext interface. Closed
here means that singletons are destroyed.
Implementing custom events can be done as well. Simply call the
PublishEvent method on the
IApplicationContext, specifying a parameter which
is an instance of your custom event argument subclass.
Let's have a look at an example. First, the
IApplicationContext:
<object id="emailer" type="Example.EmailObject">
<property name="blackList">
<list>
<value>black@list.org</value>
<value>white@list.org</value>
<value>john@doe.org</value>
</list>
</property>
</object>
<object id="blackListListener" type="Example.BlackListNotifier">
<property name="notificationAddress">
<value>spam@list.org</value>
</property>
</object>and then, the actual objects:
public class EmailObject : IApplicationContextAware {
// the blacklist
private IList blackList;
public IList BlackList
{
set { this.blackList = value; }
}
public IApplicationContext ApplicationContext
{
set { this.ctx = value; }
}
public void SendEmail(string address, string text) {
if (blackList.contains(address))
{
BlackListEvent evt = new BlackListEvent(address, text);
ctx.publishEvent(evt);
return;
}
// send email...
}
}
public class BlackListNotifier : IApplicationListener
{
// notification address
private string notificationAddress;
public string NotificationAddress
{
set { this.notificationAddress = value; }
}
public void OnApplicationEvent(ApplicationEvent evt)
{
if (evt instanceof BlackListEvent)
{
// notify appropriate person
}
}
}The IObjectFactory already offers a number of
mechanisms to control the lifecycle of objects deployed in it (such as
marker interfaces like IInitializingObject and
System.IDisposable, their configuration only
equivalents such as init-method and
destroy-method) attributes in an XmlObjectFactory
configuration, and object post-processors. In an
IApplicationContext, all of these still work, but
additional mechanisms are added for customizing behavior of objects and
the container.
All marker interfaces available with ObjectFactories still work.
The IApplicationContext does add one extra marker
interface which objects may implement,
IApplicationContextAware. An object which implements
this interface and is deployed into the context will be called back on
creation of the object, using the interface's
ApplicationContext property, and provided with a
reference to the context, which may be stored for later interaction with
the context.
Object post-processors are classes which implement the
Spring.Objects.Factory.Config.IObjectPostProcessor
interface, have already been mentioned. It
is worth mentioning again here though, that post-processors are much
more convenient to use in IApplicationContexts
than in plain IObjectFactory instances. In an
IApplicationContext, any deployed object which
implements the above marker interface is automatically detected and
registered as an object post-processor, to be called appropriately at
creation time for each object in the factory.
Object factory post-processors are classes which implement the
Spring.Objects.Factory.Config.IObjectFactoryPostProcessor
interface, have already
been mentioned... it is worth mentioning again here though, that object
factory post-processors are much more convenient to use in
IApplicationContexts. In an
IApplicationContext, any deployed object which
implements the above marker interface is automatically detected as an
object factory post-processor, to be called at the appropriate
time.
The PropertyPlaceholderConfigurer has already been
described in the context of its use within an
IObjectFactory. It is worth mentioning here
though, that it is generally more convenient to use it with an
IApplicationContext, since the context will
automatically recognize and apply any object factory post-processors,
such as this one, when they are simply deployed into it like any other
object. There is no need for a manual step to execute it.
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. This approach is shown in the example below that loads part of the configuration information from a file and another part from the assembly
IApplicationContext context = new XmlApplicationContext( "file://objects.xml", "assembly://MyAssembly/MyProject/objects-dal-layer.xml");
Generally, the Spring.NET team prefers the above approach,
assembling individual files because it keeps container configurations
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 occurences 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="..."/>
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.
The class GenericApplicationContext can be used as a basis for creating IApplicationContext implementation that read the container metadata from sources other than XML. This could be by scanning objects in a .DLL for known attributes or a scripting language that leverages a DSL to create terse IObjectDefinitions. There is a class, Spring.Objects.Factory.Support.ObjectDefinitionBuilder that does offer some convenience methods for creating an IObjectDefinition in a less verbose manner than using the RootObjectDefinition API. The following shows how configure the GenericApplicationContext to read from XML, just so show familiar API usage
GenericApplicationContext ctx = new GenericApplicationContext();
XmlObjectDefinitionReader reader = new XmlObjectDefinitionReader(ctx);
reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextB.xml");
reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextC.xml");
reader.LoadObjectDefinitions("assembly://Spring.Core.Tests/Spring.Context.Support/contextA.xml");
ctx.Refresh();The implementation of IObjectDefinitionReader is responsible for creating the configuraiton metadata, i.e implementations of RootObjectDefinition, etc. Note a web version of this application class has not yet been implementted. An example, with a yet to be created DLL scanner, that would get configuration metdata from the .dll named MyAssembly.dll located in the runtime path, would look something like this
GenericApplicationContext ctx = new GenericApplicationContext();
ObjectDefinitionScanner scanner = new ObjectDefinitionScanner(ctx);
scanner.scan("MyAssembly.dll");
ctx.refresh();Refer to the Spring API documentation for more information.
The majority of the code inside an application is best written in a
Dependency Injection (Inversion of Control) style, where that code is
served out of an IObjectFactory or
IApplicationContext container, has its own
dependencies supplied by the container when it is created, and is
completely unaware of the container. However, there is sometimes a need
for singleton (or quasi-singleton) style access to an
IObjectFactory or
IApplicationContext. For example, third party code
may try to construct new object directly without the ability to force it
to get these objects out of the IObjectFactory. Similarly, nested user
control components in a WinForm application are created inside the
generated code inside InitializeComponent. If this user control would like
to obtain reference to objects contained in the container it can use the
service locator style approach and 'reach out' from inside the code to
obtain the object it requires. (Note support for DI in winforms is under
development.)
The Spring.Context.Support.ContextRegistry
class allows you to obtain a reference to an
IApplicationContext via a static locator method.
The ContextRegistry is initialized when creating an
IApplicationContext through use of the
ContextHandler discussed previously. The simple
static method GetContext() can then be used to retrieve
the context. Alternatively, if you create an
IApplicationContext though other means you can
register it with the ContextRegistry via the method
void RegisterContext(IApplicationContext context) in
the start-up code of your application. Hierarchical context retrieval is
also supported though the use of the GetContext(string
name) method, for example:
IApplicationContex ctx = ContextRegistry.GetContext("mySubContext");This would retrieve the nested context for the context configuration shown previously.
<spring>
<context>
<resource uri="assembly://MyAssembly/MyProject/root-objects.xml"/>
<context name="mySubContext">
<resource uri="file://objects.xml"/>
</context>
</context>
</spring>The ContextRegistry.Clear() method will remove
all contexts. On .NET 2.0, this will also call the ConfigurationManager's
RefreshSection method so that the Spring context configuration section
will be reread from disk when it is retrieved again. Note that in a web
application RefeshSection will not work as advertised and you will need to
touch the web.config files to reload a configuration.
[1] More information about assembly names can be found in the Assembly Names section of the .NET Framework Developer's Guide (installed as part of the .NET SDK), or online at Microsoft's MSDN website, by searching for Assembly Names.
[3] More information about creating custom
TypeConverter implementations can be found online
at Microsoft's MSDN website, by searching for Implementing a
Type Converter.