This article is also available as a TechRepublic download, which includes code Listings A-D in a more manageable text file format.
Adobe ColdFusion has been gaining in popularity among the development community. This boom is caused by a sort of feedback loop: more powerful tools and frameworks drive application developers to become better, while better developers create more powerful tools and frameworks. One of the central elements in this ongoing success is the ColdSpring framework. In this article I introduce you to ColdSpring, explain what it is, why you should use it, and then provide some examples of its capabilities.
What is ColdSpring?
ColdSpring is a Dependency Injection framework for ColdFusion created by Chris Scott and Dave Ross (with input from other community leaders). Loosely based on Java's Spring framework, ColdSpring is used to manage the dependencies within your application’s domain model. This capability is also known as Inversion of Control, but this label seems rather unintuitive to me so I’m going to stick with Dependency Injection.
In ColdFusion, an object model is implemented using ColdFusion Components (CFCs). So in practice, ColdSpring offers a way to avoid manually and painstakingly managing the relationships between your CFCs. ColdSpring does more than just manage dependencies, but that is its primary purpose.
Why use ColdSpring?
Good object oriented (OO) design practice dictates that you should favor composition over inheritance. Following this practice generally means that your CFCs will often contain pointers to one (or many) other CFCs as instance variables. This offers a lot of flexibility in your design, but the downside is that you have to manage all of those dependencies.
Consider a domain model with two layers of functionality: a service layer a database abstraction layer. For example, a UserService might depend on a UserDAO and a UserGateway to interact with a database. In turn, your UserDAO and UserGateway CFCs might depend on a Configuration CFC to supply information about the datasource name, password, etc., as well as a Utility CFC that provides generic functions like trimming all values in a structure.
In this scenario, not only do you have dependencies between these components, but they must also be created in a specific order. The Utility and Configuration CFCs must be created first, and then made available to the UserDAO and UserGateway. Finally, the UserDAO and UserGateway must be created before they are supplied to the UserService.
Dependency injection capabilities
Let’s look at how ColdSpring can help handle the work of managing CFC dependencies. You can define your dependencies in several ways, but the most common method is using an XML file. Initializing ColdSpring in your code is very easy. (Listing A)
<cfset serviceDefinitionLocation = expandPath('services.xml' ) />
<cfset coldSpring = createObject( 'component', 'coldspring.beans.DefaultXmlBeanFactory').init() />
<cfset coldSpring.loadBeansFromXmlFile(serviceDefinitionLocation) />
Once that is done, ColdSpring is ready for use. To have ColdSpring create a CFC for you, you simply call the getBean() method and pass in the name of the component you want, like this: (Listing B)
<cfset test = coldSpring.getBean('test')/>
I’m going to show some sample ColdSpring XML, and I’ll go through each piece of it and discuss what ColdSpring will do in response (Listing C).
<?xml version="1.0" encoding="UTF-8"?>
<bean id="user" class="tests.articlecode.coldspring.User">
<bean id="utility" class="tests.articlecode.coldspring.Utility"/>
<bean id="testFactory" class="tests.articlecode.coldspring.TestFactory"/>
<bean id="test" factory-bean="testFactory" factory-method="getInstance">
There are several different features being used in the preceding XML. First, note that ColdSpring identifies the various components as "beans", which is mainly just a carry-over from Spring. I sometimes wish this name had been a bit more descriptive because there can be some confusion about what a "bean" is supposed to be. However, if you just think of these as CFCs you’ll be fine.
The first bean definition is for a simple CFC called User. User has a property called Utility. If you look below this bean definition, you’ll see that the second one defines the utility CFC. So right off the bat, ColdSpring will:
- Create an instance of Utility
- Create an instance of User
- Because we’ve told ColdSpring that the User CFC depends on Utility, ColdSpring will automatically look for a setter method that matches the name of the injected bean (in this case, setUtility())
- Finally, ColdSpring will inject Utility into User by called a setUtility() method on the User, passing the Utility instance as an argument. This is called setter injection
So ColdSpring will not only resolve the dependencies for you, but it even resolves them in the correct order automatically. However, there are other ways to get a new CFC from ColdSpring, as the third and fourth bean definitions show.
The third definition in Listing C above defines a CFC called TestFactory. This is, as you might guess, a Factory, meaning that its job is to create other CFCs. You might wonder "why do we need factories? Isn’t ColdSpring a factory already?"
Yes, ColdSpring is a factory, but it is a simple factory. By allowing us to call our own custom Factories from ColdSpring, we have much more control and power over how the CFCs are created. Just consider a simple example, where we might want a ContactManager CFC to allow the server to send communication to a system administrator. During work hours it might create one CFC that deals with e-mail, but after working hours it might create a CFC that sends pages or text messages. It would be simple to add some conditional logic to our Factory to instantiate different CFCs based on different contexts.
As you can see from the final bean definition, I’m using the TestFactory CFC and having ColdSpring call the getInstance() method on it. I’m also passing some data directly into the factory method: a structure named parameters with a key called User and a value of the User CFC we created earlier.
You can use this approach, called constructor injection, to inject dependencies into any of your beans instead of the previously-demonstrated setter injection approach. Both approaches have their pros and cons, but one advantage of setter injection over constructor injection is that you can avoid issues with circular dependencies. Circular dependencies can arise, for example, if CFC1 needs CFC2 as a constructor argument, but CFC2 needs CFC1 as a constructor argument.
Returning to the example, we now have a good idea of how ColdSpring will respond to the XML bean definitions we’ve given it. Now all we have to do is use it. (Listing D)
<cfset test = coldSpring.getBean('test') />
So to sum up, in response to this call to getBean, ColdSpring will:
- Create an instance of Utility
- Create an instance of User, and inject Utility into user.setUtility()
- Create an instance of the TestFactory
- Call testFactory.getInstance() and pass in a structure of data as an argument, which will return a CFC called Test.
OK, so what?
All of this is a contrived example meant to demonstrate a few of the different ways ColdSpring can be used. But you might say, "I could do all that myself just by creating the objects by hand!" And you surely could. The benefits of something like ColdSpring don’t really become fully obvious until you are dealing with a large set of CFCs.
Managing a couple of CFCs, like the example, is trivial. Managing the dependencies between 200 CFCs is quite another story. If you create a CFC in the wrong order, it all goes boom. Forget to pass a CFC correctly and it all goes boom. ColdSpring can do this work for you. Finally, the ColdSpring XML becomes a terrific map of your CFC dependencies. Instead of sifting through dozens of components to try and figure out what depends on what, you can see it all in one place in the XML file.
Most of the popular MVC frameworks for ColdFusion directly support ColdSpring via plugins or adapters. Many even support "autowiring", so that after you’ve defined your beans in the XML, the plugins will attempt to supply the ColdSpring-generated beans to any of your framework components by looking for setters that match the bean names. Model-Glue, Mach-II, and Fusebox can all support this capability.
On top of its dependency injection capabilities, ColdSpring also introduces Aspect-Oriented Programming (AOP) to ColdFusion users. AOP is a powerful approach to solving certain kinds of problems. The common poster-child for AOP is logging. You might want to write to log file when certain methods are executed. In standard OO, you’d have to add code to each of those methods somewhere to handle the logging.
AOP turns this on its head. Instead of adding logging code all over the place, you define your logging code once, and then use AOP to intercept method calls throughout your model and run your logging code in addition to the method call.
In practice, ColdSpring essentially creates a wrapper CFC that extends your original CFC and returns this wrapper component to you when you call for that CFC. None of your code ever knows it is dealing with the wrapper instead of the original component; ColdSpring manages all of this transparently. It creates proxies for all of the original CFC’s methods, but lets you add additional code (like logging) before or after the method executes.
Barney Boisvert has a nice working example of some AOP code that automatically encloses any method calls you desire within cftransaction blocks. This allows you to automatically add or remove database transaction management to your method calls! You can find this code in his blog.
One final example of the AOP capabilities is the RemoteFactoryBean. Using AOP, you can take any of your model components and generate a remote Façade component for it. This allows you to open up some or all of your methods for invocation from remote sources such as Web services, AJAX, or Flash Remoting calls. You can even use ColdSpring to automatically convert CFC data into ActionScript objects using the FlashUtilityService.
I’ve only been able to touch on the major features of ColdSpring, but I hope you can see that it brings a lot of very powerful capabilities to the table. It is a well tested and stable dependency injection and AOP implementation for ColdFusion. You can download ColdSpring, sample code, and read full documentation. There are also links to the CVS repository and a mailing list devoted to the topic. Give it a try for yourself and see what all the fuss is about!