I've been working with a large open-source java application lately that uses several interesting technologies, namely Hibernate (object-relational mapper), Java Server Faces (ui framework/toolkit), and Spring. The app doesn't use all of Spring's functionality, only the "managed-beans" portion of the framework.

"Managed-beans" may not be the best term for what Spring does for us, as it's more likely used in a "managed-services" fashion, where all the "beans" are "service" components, and usually end up being singletons (only one instance exists for the entire lifetime of the application).

What spring managed-beans can do for you is manage the dependencies between your components in a clear, concise fashion (typically done with an XML configuration file). It eliminates the need for your services to go off looking for something they need, or for you to provide it when you instantiate a service. It does this through something called "inversion of control" (IoC), in which a container (spring) is a in charge of instantiating and resolving the dependencies of your services, rather than leaving that job to the services themselves (or the code that instantiates/uses those services). Lets try to look at an example from the CF point of view:

Lets say we have a "service" cfc that talks to your ldap directory. It might have some methods like getUser(...) etc. Typically you would instantiate the cfc and provide some information about HOW that cfc should talk to the ldap server (ip, port, etc). So you have something like:

<cfset application.ldapService = createObject("component","path.to.ldapService").init()/>
<cfset application.ldapService.setLdapIp("10.10.10.10")/>
<cfset application.ldapService.setLdapPort(389)/>

Maybe you abstracted things a little further, and made your ldap settings into it's own cfc (because there were just too many properties to manage and you wanted to use them in several places)

<cfset ldapSettings = createObject("component","path.to.ldapSettings").init()/>
<cfset ldapSettings.setIp("10.10.10.10")/>
<cfset ldapSettings.setPort(389)/>
<cfset ldapSettings.setBasePath("o=Something,ou=SomethingElse")/>

<cfset application.ldapService1 = createObject("component","path.to.ldapService1").init()/>
<cfset application.ldapService1.setLdapSettings(ldapSettings)/>

<cfset application.ldapService2 = createObject("component","path.to.ldapService2").init()/>
<cfset application.ldapService2.setLdapSettings(ldapSettings)/>

As I'm sure many know, this can get a lot more messy than what's laid out above. You could have hundreds of services, with so many different configurations and dependencies flying around that things get ugly, fast. Spring/IoC can really come to rescue here, because it can consolidate the mess into easy to read xml, and it can eliminate having to programmatically configure your application's components, like above. If I were using a spring-like CF-based inversion of control container, i would first have some xml that describes my ldap settings cfc:

<bean id="ldapSettings" class="path.to.ldapSettings">
  <property name="ip"><value>10.10.10.0</value><property>
  <property name="port"><value>389</value><property>  
  <property name="basePath"><value>o=Something,ou=SomethingElse</value><property>
</bean>

Then, I would define my ldapService cfcs like so:

 <bean id="ldapService1" class="path.to.ldapService1">
  <property name="ldapSettings"><ref bean="ldapSettings"/><property>
 </bean>

 <bean id="ldapService2" class="path.to.ldapService2">
  <property name="ldapSettings"><ref bean="ldapSettings"/><property>
 </bean>

Spring will look at the properties you define and call the appropriate setter-methods to weave things together. So, when asked for say "ldapService1", it will create an instance of ldap settings, set it's properties, and pass it into setLdapSettings(...) on the ldapService1 cfc. In your code, rather than using the createObject function to instantiate your cfc, you would ask a spring "bean factory" for it (i'll call it the "service factory" since that makes more sense).

<cfset application.ldapService1 = serviceFactory.getBean('ldapService1')/>
<cfset application.ldapService2 = serviceFactory.getBean('ldapService2')/>


Wait a second, rather than using setters, I usually pass dependencies or config values into my services as arguments to the constructor ("init()" if we are talking CF), like this:

<cfset ldapSettings = createObject("component","path.to.ldapSettings").init("10.10.10.10",
                                                                                                                389,
                                                                                                                "o=this,ou=that")/>

<cfset application.ldapService1 = createObject("component","path.to.ldapService1").init(ldapSettings)/>

Luckily, spring has you covered, your xml would just look like this instead:

<bean id="ldapSettings" class="path.to.ldapSettings">
  <constructor-arg name="ip"><value>10.10.10.0</value></constructor-arg>
  <constructor-arg name="port"><value>389</value></constructor-arg>  
  <constructor-arg name="basePath"><value>o=Something,ou=SomethingElse</value></constructor-arg>
</bean>

<bean id="ldapService1" class="path.to.ldapService1">
  <constructor-arg name="ldapSettings"><ref bean="ldapSettings"/></constructor-arg>
</bean>

And that's what's cool about Spring/IoC... you shouldn't have to change what you've done, it just allows you to reorganize things so there is less configuration/instantiation/etc. code. So, hopefully you can see the benefits of what IoC does. I already have a functional proof of concept for coldfusion, and a plugin for mach-ii to help integration between the two. Post a comment/shoot me an email if you're interested in taking a look at what I've done so far. More to come...


12/27/2004 09:50 P - Greg Narain said...
Hi, I've been doing something like this for a year plus now.. wish I had known about Spring. I'd love to take a peek at what you've gotten down. Best regards, Greg

12/27/2004 10:12 P - Brandon Smith said...

A collegue and I came up with a framework for ColdFusion inspired by the likes of IoC. Granted it is not to the scale of many of these containers, but it definately has some qualities worth checking out.

The project resides at http://hollywood.tigris.org

The current release can be found at http://hollywood.tigris.org/files/documents/1725/19651/HW.rar

We would be happy to answer any questions that any may have.

Brandon Smith


12/27/2004 10:36 P - Dave Ross said...

Brandon,

I actually found the Hollywood project last week when I first started thinking about this... I couldn't find much info about it(asked on cfcdev as well).

Peeking at the source, ithink we may be looking at different goals. I have no desire to write a MVC web-app framework, I feel as though we've got one that's "good enough" (mach-ii). That being said, it seems a lot of features that you haven't implemented (yet) are the first one's I set out to tackle: dependency injection, autowiring, property resolution.

I'm attempting to keep this as lightweight as possible, so perhaps if you haven't taken a stab at those you could drop in what I have (basically you would use a beanFactory in your hw-core.requestObject to manage your "objectStack" instances).

I'll shoot it to ya tommorrow... I have some stuff on my work machine that needs to get comitted first.


12/28/2004 04:15 A - Brett Gullan said...
I started to build a similar extension for Mach-II, based in part on the Fulcrum/Avalon projects in Apache Jakarta. I only really got as far as using an XML file to define and configure services for the framework to instantiate, so I'd love to have a look at your approach... Regards, Brett.

02/04/2005 06:54 A - Robin Hilliard said...
I've also been looking at elements of Spring, I was actually planning to talk about Spring's auto-wire feature in my upcoming talk at MXDU, in which I'm also talking about how to fake interfaces - Rod Johnson seems to be big fan of "program to interface, not implementation".  Anyway, good to see others cottoning onto Spring, it's an eminently sensible framework for J2EE development.

Robin

02/04/2005 07:27 A - Dave Ross said...

Yes! The task I have ahead is proving that (a subset of) Spring's features will work in CF without native interfaces.

To this point I'm actually happy with what I've done, because it does everything that I needed it to at the time (auto-wiring, etc). Now I need to figure out what everyone else needs!


Post a comment:

(required, will not be displayed)
 


   You will be sent an email asking you to validate your comment.



Driven by Farcry Open Source CMS. Dressed in Aura.
Powered by ColdFusion MX.