In case you didn't know, Spring Framework was the first publicly available IoC container, originally designed by a Java author and programmer Rod Johnson back in 2002. Even before that, there was Dependency Inversion Principle, invented by Uncle Bob in 1994. For history-inclined geeks, this IoC history article on PicoContainer site goes into great details on who and when contributed to this fantastic idea.
I've been a happy user of Spring Framework's .NET port for a while. I loved its non-invasive philosophy and was very happy with its rich feature set. As a matter of fact, features are not the reason for my migration to Castle Windsor. My concern is the community around these two projects, with Castle crowd having grown dramatically since 2004/2005 while Spring.NET arguably remaining Mark Pollack's sickly baby. If this dynamics persists, and I don't see any reasons to think otherwise, the .NET branch of Spring project will be shut down in a few years, and by that time I want to be completely off the hook.
In this post I will summarize my notes as I migrate my latest project from Spring Framework.NET to Castle Windsor IoC. (Brief synopsis for those who won't read entire article: Windsor turned out to be surprisingly similar to Spring, but much simpler, with easier syntaxis and very minimalistic features. At the bottom of the post I list advanced facilities I use in Spring that I still don't know how to port to Windsor.)
First of all, you want to remove the reference to Spring.Core.dll and add four references instead (I wonder why they couldn't ILMerge them into one):
- Castle.Core.dll
- Castle.MicroKernel.dll
- Castle.Windsor.dll
- Castle.DynamicProxy.dll
Next, in your application startup code, initialize Windsor instead of Spring:
IWindsorContainer container = new WindsorContainer(new XmlInterpreter(new ConfigResource("castle")));
This assumes you were using resource tag to reference your Spring.Config from your App.Config. If you were loading your Spring.Config file directly from a well known location, Windsor supports this option as well, just use an overload of XmlInterpreter that takes file name.
Next, you'll need to change your App.Config to point to Windsor config files, instead of Spring:
<configSections>
<section name="castle" type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />
</configSections>
<castle>
<include uri="file://Windsor.config">
</castle>
Again, this is a direct port from my way of bootstraping Spring. For other options, check out Windsor documentation for the include tag.
This done, let's look inside Windsor config file and see how it is different from Spring.config. In Windsor, what was called object in Spring.NET (or bean in the original Spring), is now called component. Note how parameters are specified using a better-looking parameters tag and how neatly the object references (now called service lookups) are expressed using the special ${} syntax:
<configuration>
<components>
<component id="componentA" type="zvolkov.migrateToWindsor.componentA, zvolkov.migrateToWindsor">
</component>
<component id="componentB" type="zvolkov.migrateToWindsor.componentB, zvolkov.migrateToWindsor">
<parameters>
<myComponentA>${componentA}</notifier>
<otherProperty>bloody-blah</otherProperty>
<anArrayProperty>
<array>
<item>XXX</item>
<item>YYY</item>
</array>
</anArrayProperty>
</parameters>
</component>
</components>
</configuration>
You see how much better this is than Spring's <property name="myComponentA" ref="componentA"/>? Looks so much nicer! Also cool is that parameters tag can be used for both property and constructor injection. One less syntax to remember!
If your "bloody-blah" is a setting used by multiple components, you can promote it to a global property like so, note the #{} syntax:
<properties>
<bloody>bloody-blah</bloody>
</properties>
<component id="componentB" type="zvolkov.migrateToWindsor.componentB, zvolkov.migrateToWindsor">
<parameters>
<otherProperty>#{bloody}</otherProperty>
</parameters>
</component>
Other useful but scary feature is auto-wiring: You don't have to specify references to objects! If you don't, Windsor will auto-wire your property to an object matching the property type. So you don't have to use service lookups unless your container has more than one object of the same type (and even then Windsor will default to the top one object in the container). I'm sure Windsor veterans consider this a power feature but IMHO it may be very dangerous especially if you start with one implementation of an interface and add more later. As a matter of fact, Windsor's auto-wiring is so advanced that it even supports generic specialization, a feature best illustrated by Ayende in his historical MSDN article.
Coming back to basics, passing arrays, lists and dictionaries is pretty much same as in Spring. Here's the syntax you need to use for those. The lifestyles of objects also same story: by default all objects are singletons and you can change that by specifying lifestyle attribute on the component tag.
To summarize, my first impression is pretty good. To the end user Windsor looks very simple, with no unneeded features, easy to read and write syntax, and super-lazy auto-wiring. In a next post I'll try to cover more advanced facilities that I will need to migrate from Spring as well:
- NHibernate configuration,
- NHibernate Session management and (most importantly) nested Transaction management,
- Quartz.NET integration,
- WCF integration
I believe this post covers the first steps you need to take and provides enough information to drive the fear of the unknown away. So stop procrastinating and move to Windsor before it's too late. This link to Windsor documentation will keep you going.
Tags: