Overview
This article demonstrates the core feature of Spring .NET
that have been ported to the .NET platform as well as many of the .NET specific
features that have been given the Spring .NET treatment. Spring .NET is an
application framework which helps to build enterprise .NET applications. It
revolutionizes the way .NET applications are configured, deployed and hosted.
Spring .NET offers a lightweight Dependency Injection container built around
the concept of the factories Design pattern.
Its "inversion of control" model (this is the other
name of Dependency Injection) has a lot to offer if the team is struggling
through issues of over-coupling and objects that are hard to unit test.
Dependency Injection (DI) allows objects to be injected into a class, rather
than relying on the class to create the object itself. It also provides a suite
of services that can be applied to any object under Spring .NET's control. And
since Spring .NET is built using standard .NET based code, applications that
utilize Spring.NET have no additional dependency on COM, COM+, or Enterprise
Services.
Software Requirements
.NET Framework 1.1
Spring
.NET 1.1
A Spring .NET implementation
The example below demonstrates how to achieve the Dependency
Injection using the Spring .NET framework. Implementation of Spring .NET
consists of the following steps.
·
Defining the Interface
·
Classes implementing the Interface
·
Setting up the Config file
·
Creating the Client application to utilize the Spring .NET features
·
Defining the Interface
The code snippet in Listing 1 is a simple interface, which
the objects will implement.
Listing 1
public interface IDomainObjectInterface
{
string Name
{
get;
}
}
This Interface contains one property, which returns a string
representation of the name of the object.
Classes implementing the Interface
The code in Listing 2 contains two classes,
ImplementationClassA and ImplementationClassB which each implement the
interface. In these Implementation Classes, there is a Name property which
simply returns the name of the class, depending on which concrete class is
used. This Interface and implementation classes will be used to demonstrate the
advantage of using the Spring .NET implementation.
Listing 2
public class ImplementationClassA:IObjectInterface
{
public ImplementationClassA(){}
public string Name
{
get
{
return "Implementation Class A";
}
}
}
public class ImplementationClassB:IObjectInterface
{
public ImplementationClassB(){}
public string Name
{
get
{
return "Implementation Class B";
}
}
}
Setting up the Config file
Now, add the config.xml file, which drives the Dependency
Injection. Listing 3 shows the full config.xml file:
Listing 3
<?xml version="1.0"encoding="utf-8" ?>
<objectsxmlns="http://www.springframework.net">
<objectname="DomainObjectImplementationClass"
singleton="false"
type="ImplementationClassA,SpringDIExample" />
</objects>
The configuration file consists of only two elements,
<objects>, which contain all of the object definitions, and the
individual <object> definitions. In the configuration file shown, there
is one object definition. The object definition consists of three basic
attributes which define what object is created and how it's created.
The name attribute defines the name that the Windows
Application class uses when requesting an object from the config.xml file. In
this case, it is "DomainObjectImplementationClass". This name is just
used to reference the definitions contained in the configuration file from the
client code.
Next, the singleton attribute is a Boolean flag which
signifies if the object should be created as a singleton or not. Spring .NET
has built-in support for making objects singletons. When this attribute is set
to true, it indicates the object will be created as
Singleton and when it is set to false, object will
not be created as Singleton.
Finally, the type attribute defines the actual type of the
object to be created. This object type will actually be loaded and returned
when the config.xml file is queried. This string will be in the form of "Type,
Assembly", wherein Type indicates the type of object and the Assembly
indicates the Assembly Name which contains the object type. As indicated in the
configuration file, the type desired is one of the types shown in Listing 2.
Creating the Client application to utilize the Spring.Net
features
Here we will create a Window Application client. Add the
form in the Windows Application project and add a button to the form and name
the button as btnSpringNETImplementation. Paste the code (Listing 4) in the
click event of the button. The code snippet demonstrates calling the
Implementation classes from the config file. This adds a significant amount of
flexibility and functionality to the application.
Listing 4
using System;
using System.IO;
using Spring.Objects.Factory.Xml;
using SpringDIExample;
namespace WindowsApplication1
{
public partial class Form1: Form
{
public Form1()
{
InitializeComponent();
}
private voidbtnSpringNETImplementation_Click(object sender, EventArgs e)
{
using(Stream stream = File.OpenRead("config.xml"))
{
XmlObjectFactory xmlObjectFactory = newXmlObjectFactory(stream);
IObjectInterface domainObjectInterface =(IObjectInterface)
xmlObjectFactory.GetObject("DomainObjectImplementationClass");
MessageBox.Show("Name is " +domainObjectInterface.Name);
}
}
}
}
To use the Spring .NET Dependency injection container
requires the initial setup. In the listing 4, firstly Config.xml file is opened
and the object definitions are read.
Next, create the new object factory using a config.xml file
as the source of your object definitions. Then call the Implementation Class
with generic label for the object and upcast the object instances returned by
the factory to the interface that you expect.
Finally, the last line of the MessageBox.Show remains
unaffected, even though you have changed the source of the object and how it's
instantiated.
Run Listing 4 and click SpringNETImplementation button in
the form, which displays “Implementation Class A” message.
Now let's see the advantage of Spring .NET and the
Dependency Injection: modify Config.xml file (Refer Listing 5) as shown below.
The only difference lies within the type attribute.
Listing 5
<?xml version="1.0"encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<objectname="DomainObjectImplementationClass"
singleton="false"
type="ImplementationClassB,SpringDIExample" />
</objects>
Now again Run the Windows Application1 in Listing 4 and
click SpringNETImplementation which displays “Implementation Class B”.
By just changing the type of the implementation class in the
configuration file from "ImplementationClassA" to
"ImplementationClassB", we can dynamically alter the class that is
returned to the client, all without a recompile.
Editor's Note: A similar system is
used throughout the ASP.NET v2 architecture, where it is commonly referred to
as the "Provider Model".
Extensibility Enhancement Feature of Spring.NET
In this case, IObjectInterface is published in a public API,
and would like to allow users of the API to create their own implementations of
the interface, all while still utilizing several existing clients that have
already been built to use the IObjectInterface. Getting the users'
implementation to your clients could prove to be difficult, especially if we do
not know how the class will be built or deployed. ImplementationClassC is a
third implementation of the IObjectInterface, which is similar to the
ImplementationClassA and ImplementationClassB classes, except this one resides
in a separate assembly. Previously, both implementation classes and the
interface resided in the same assembly:
Listing 6
public class ImplementationClassC:IObjectInterface
{
public ImplementationClassC(){}
public string Name
{
get
{
return "Implementation Class C";
}
}
}
Now, we will again use the above Windows Application1 to
demonstrate how to call ImplementationClassC using Spring.NET framework. This
requires a minor change to the original config.xml configuration file. The
following is the configuration file with the necessary changes made. Again the
only difference lies within the type attribute, which has been updated to point
to the ImplemenationClassC type and the SimpleDIExampleExtension assembly:
Listing 7
<?xml version="1.0"encoding="utf-8" ?>
<objectsxmlns="http://www.springframework.net">
<objectname="DomainObjectImplementationClass"
singleton="false"
type="ImplementationClassC,SpringDIExampleExtension"/>
</objects>
When we rerun the Windows Application1 and click the SpringNETImplementation
button on the form, an instance of ImplementationClassC will be instantiated
and returned. This is accomplished without recompiling the Windows Application,
even though ImplementationClassC resides in an assembly that's physically
separate from the original implementation classes.
Conclusion
In this article, we have demonstrated how to utilize
Dependency Injection features with Spring .NET. The Dependency Injection
pattern simplifies application code, and increases configuration flexibility by
deferring component configuration and assembly to the container.
This lightweight container helps to assemble components from
different projects into a cohesive application. It also saves you from writing
boilerplate factory creation code over and over again. Spring .NET is an
example of a framework that provides a ready to use DI container.
It's true that this pattern (and many others found
throughout the .NET Framework) does not have enough advertising, and that may
be the cause for their scarce usage in .NET application architectures.
Hopefully this article will help many more developers learn about this powerful
technique and toolset.
Resources
Spring.NET