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...
| ||
| ||
| ||
|
| ||
| ||
| ||