Cold Spring Reference
Cold Spring Reference
Cold Spring Reference
1) A setter method that will accept the TaxCalculator instance. 2) An argument to the ShoppingCartManager constructor that will accept the
TaxCalculator instance.
Heres a before/after example of what your ShoppingCartManager constructor might look like: Before ColdSpring:
<cffunction name=init returntype=myApp.model.ShoppingCartManager output=false hint=constructor> <cfargument name=MaxItems type=numeric required=true hint=max items for any of my users carts/>
After ColdSpring:
<cffunction name=init returntype=myApp.components.ShoppingCartManager output=false hint=constructor> <cfargument name=TaxCalculator type=myApp.model.TaxCalculator required=true hint=Dependency: TaxCalculator/> <cfargument name=MaxItems type=numeric required=true hint=max items for any users cart/> <cfset variables.TaxCalculator = arguments.TaxCalculator/> <cfset variables.MaxItems = arguments.MaxItems/> </cffunction>
Ok, so this doesnt *look* like any less code, nor does it look any cleaner. Maybe some added complexity will start to reveal the differences. Lets say our TaxCalculator needs to be told the current tax rate in order to function properly. Without ColdSpring, our constructor changes to:
<cffunction name=init returntype=myApp.model.ShoppingCartManager output=false hint=constructor> <cfargument name=TaxRate type=numeric required=true hint=Current Tax Rate/> <cfargument name=MaxItems type=numeric required=true hint=max items for any users cart/> <cfset variables.TaxCalculator = createObject(component,myApp.components.TaxCalculator).init( arguments.TaxRate )/> <cfset variables.MaxItems = arguments.MaxItems/> </cffunction>
Does the ColdSpring version change? Actually, it does not! In fact, you might have noticed that the ShoppingCartManager is now receiving a tax rate (and passing it to the TaxCalculator), which to me is outside the scope of what the ShoppingCartManager is meant to do. In the ColdSpring example, the TaxCalculator comes in ready-to-use, and the ShoppingCartManager is not burdened with creating and configuring it. So, why is the dependency-injection approach better? components arent asked to do things outside of their scope or duty (known as separation of concerns) components arent completely tied to other implementations (again, less coupling) components are easier to configure and you can do so without changing code components become easier to test (we can dictate which collaborators they use, perhaps even creating dummy stub or mock objects to trick the component into thinking that its running in a different environment). you can get a birds eye view of the dependencies among your components (and generate some neat documentation) components are not tied to ColdSpring at all. There should be very little plumbing required to use ColdSpring in any environment, and only calling code will be aware of its existence. In Model-View-Controller apps, this usually means that the Controller will have some knowledge of ColdSpring but nothing else will.
To use ColdSpring with your components, you simple create a configuration file (or files) that contain some simple xml tags which tell ColdSpring about your components (and their dependencies and configuration). Take a look at the following xml snippet:
<bean id=ShoppingCartManager class=myApp.model.ShoppingCartManager/>
A ColdSpring bean tag is how you define your component(s), but dont read too much into the nomenclature. ColdSpring uses the <bean/> syntax because it relies heavily on the Java-beans spec to resolve its dependencies (especially public properties exposed via a setter-method). All the above line says is hey, ColdSpring register myApp.components.ShoppingCartManager under the name ShoppingCartManager. This means at any time you can ask ColdSpring to give you the ShoppingCartManager and it will return a myApp.components.ShoppingCartManager instance. Typically this instance is usually shared among all that ask (aka a singleton), however you can define your bean such that each time your code asks for the bean it will receive a new instance. Your bean definition will probably be a bit more complex, e.g. you might have some configuration details to pass to the ShoppingCartManager perhaps into the constructor (your CFCs init() method), shown in this <bean/> snippet:
<bean id=ShoppingCartManager class=myApp.model.ShoppingCartManager> <constructor-arg name=MaxItems><value>15</value></constructor-arg> </bean>
or as a property (e.g., a setterMethod you would need to have a setMaxItems(items) method for the following to work):
<bean id=ShoppingCartManager class=myApp.components.ShoppingCartManager> <property name=MaxItems><value>15</value></property> </bean>
Ok, lets look back at our problem we need to inject a tax calculator into our ShoppingCartManager. This is simpler than you think we have to define the TaxCalculator first (well supply it with the current tax rate):
<bean id=TaxCalculator class=myApp.components.TaxCalculator> <constructor-arg name=TaxRate><value>0.8</value></constructor-arg> </bean>
Then, in our ShoppingCartManager bean definition, we can reference the TaxCalculator by using the ref tag:
<bean id=ShoppingCartManager class=myApp.components.ShoppingCartManager> <constructor-arg name=MaxItems><value>15</value></constructor-arg> <constructor-arg name=TaxCalculator><ref bean=TaxCalculator/></constructor-arg> </bean>
Now, when ColdSpring creates the ShoppingCartManager, it will pass in (inject) the TaxCalculator instance. The ShoppingCartManager has no idea where the TaxCalculator came from but it is perfectly happy to use it. Youve now removed
unnecessary code from the ShoppingCartManager, and loosened its coupling to the TaxCalculator, making your code more reusable, testable, and maintainable.
You should be able to run the above line of code without error if ColdSpring is installed correctly on your server. II.II Supplying the BeanFactory with your bean definitions The DefaultXmlBeanFactory implementation can only read bean definitions from xml. There is no way to programmatically add bean definitions to this implementation (however one could construct the necessary xml on the fly and give that to the DefaultXmlBeanFactory). Currently, there are 3 ways to add bean definitions to the DefaultXmlBeanFactory: 1) Pass the DefaultXmlBeanFactory a fully qualified path to a bean definition xml file.
<!--- void loadBeansFromXmlFile(string beanDefinitionFile, boolean ConstructNonLazyBeans) --->
Youre probably wondering what ConstructNonLazyBeans does, but first Ill explain the basics of configuring the DefaultXmlBeanFactory and the beans you put in it.
II.III
To explore all of the attributes of a ColdSpring bean definition, one could look at the J2EE Spring frameworks DTD (which ColdSpring expects you to adhere to). However, not every attribute or tag is fully implemented in ColdSpring, and there are some that arent applicable to CFC development, so they are simply ignored. ColdSpring beans are defined via the <bean/> tag, and here are the attributes of the <bean/> tag worth mentioning (attributes in bold are required): Attribute Name Description and Use Implemented/ Planned/ Wont Implement
id
name
class
singleton
init-method
lazy-init
destroy-method autowire
This is the identifier used to store your bean. When you ask ColdSpring to give you a reference to one of its beans, youll use this same identifier. Serves the same purpose as id, however can accept multiple identifiers via a comma separated list. This effectively allows you to define your bean as having several aliases. The actual CFC type to create for this bean definition. true|false When true, indicates whether one shared instance of your bean will be kept by the BeanFactory and returned to all retrieval requests. When false, a new instance will be created and returned to each retrieval request. A name of a method that ColdSpring will call on a bean after all its dependencies have been set. Since we use init as a constructor in CFCs, setup or configure are good alternatives. If your CFC needs to do something with one or more of its dependencies immediately after receiving them, init-method is the easiest way to do it. true|false When true, ColdSpring wont create the bean (or any dependencies of the bean that havent been created) until it is asked for the bean. When false, ColdSpring will create the bean immediately upon receiving its definition (unless the method used to populate the BeanFactory tells ColdSpring not to via. the ConstructNonLazy beans argument - see II.II) If implemented, ColdSpring would call this method on a bean before it is destroyed. no|byName|byType Tells ColdSpring to autowire in dependencies by looking at the public properties (setters) of your bean and seeing if it knows about a bean that would match the signature. It will look for a match either by the name (or id) of a bean, or by the beans type. If implemented, would explicitly tell the beanFactory to fully instantiate the bean specified by this attribute before creating the bean that defines depends-on. Causes ColdSpring to call this method on the class defined in the bean to return the actual instance to use for this bean.
Implemented
depends-on factory-method
factory-bean
id of a bean, known to Coldspring, on which the specified factory-method would be called to obtain an instance.
Wont Implement Implemented (except for constructor and autodetect values). Wont Implement Wont Implement (by itself, see factory-bean) Implemented (use with factorymethod)
Those are the standard attributes of a bean tag. There are a few others that are seriously outside the scope of ColdSpring due to the differences between CFCs and Java classes, so I wont mention them here.
II.IV The
There are only two available child-tags of <bean/>, typically used to express dependencies among your beans or to supply the bean with some type of data (usually configuration information, or placeholders for configuration). The <bean/> child tags implemented in ColdSpring are:
1)
<constructor-arg name=argName/>
This tag will cause Coldspring to supply your bean with a value or object reference when it is instantiated (during a CFCs init() method), passed as an argument named via the name attribute.
<property name=propertyName />
2)
Similar in nature to constructor arg, however in this case ColdSpring will pass some value or object reference into your bean as an argument to a setter method, identified via the name attribute. Thus, your CFC must have a setter method name that matches the property tags name attribute (for example if your property is named foo then your CFC needs a setFoo() method).
The <lookup-method /> tag has yet to be implemented in ColdSpring. If you are interested in what it does, it is a way of injecting a method into a CFC that can then be used by that CFC to retrieve a bean from the factory. Think of it like mailing someone a cellphone with your number punched in rather then calling them directly. II.V Children of
Both <constructor-arg/> and <property/> can accept a wide range of child tags, used to define what values or object references need to be passed into the constructor argument or property setter, respectively. The table below lists all currently available child tags to both <constructor-arg/> and <property/> Tag
<value></value>
Example Usage
<value>15</value> <value>${key.subKey}</value>
<map/>
Description Used to pass in an arbitrary value, defined either directly in the xml or in the defaultProperties supplied to the BeanFactory. Used to pass in a reference to another bean defined within the BeanFactory. Can be used to define an entire bean to be used only for the purpose of injecting into another bean. All attributes and child tags will be available. Will pass a struct into your bean. Each entry within the map will correspond to a key within the passed-in struct. The child tags of <entry/> are any tags listed here, including map. Since CF only supports simple values
for struct keys, only the key= attribute of entry is supported. Like <map/>, but an array instead of a struct. Child tags can include anything listed here ( including <map/> and <list/> )
The other tags specified by the Spring DTD, <null/>, <props/>, and <set/> are either yet-to-be implemented or wont be implemented in ColdSpring. There is no limit to the depth of your bean definitions, demonstrated by this example snippet (which will work provided the CFCs exist):
<bean id=bean1 class=path.to.bean1> <constructor-arg name=bean2> <bean id=bean2 class=path.to.bean2> <property name=bean3> <bean id=bean3 class=path.to.bean3> <property name=bean4> <ref bean=bean4/> </property> </bean> </property> </bean> </constructor> </bean> <bean id=bean4 class=path.to.bean4/>
b. Then define your component that needs the NotificationService, and pass in the reference via constructor-arg or
property (property is shown)
<bean id=ComponentThatNeedsNotificationService class=myApp.model.ComponentThatNeedsNotificationService> <property name=NotificationService> <ref bean=NotificationService/> </property> </bean>
The <cfset variables.notificationService = arguments.notificationService/> line makes sure ComponentThatNeedsNotificationService will retain a reference to the notification service so that it can be used until overwritten or ComponentThatNeedsNotificationService is destroyed. Thus anywhere ComponentThatNeedsNotificationService needs to send a notification it simply says something like: <cfset variables.notificationService.send() /> III.II ColdSpring and MVC frameworks ColdSpring was also developed to fit in well with existing MVC (Model View Controller) frameworks, such as Fusebox 4 and Mach-II. To use ColdSpring with one of these frameworks, its important to understand the big picture, as in where ColdSpring sits in relation to the rest of the application. The following diagram illustrates ColdSprings relationship in a MVC web application:
Why does the Controller layer need to communicate with ColdSpring? In some cases, it may only be to retrieve objects (beans) from the ColdSpring bean factory. In others, the Controller may handle setting up and configuring the bean factory. Either way, its important to note that once the controller obtains a reference to a bean, it does not communicate thru ColdSpring. For those who use the Mach-II framework for a controller, ColdSpring ships with a Mach-II plugin (coldspring.machii.coldspringPlugin.cfc) that automates the creation of the bean factory. The following diagram details MachII and ColdSpring together:
You must let the plugin know the location of your bean definitions xml file, and the plugin will handle the rest: creating the bean factory, passing in the bean definitions, and then placing the whole thing in Mach-IIs property manager. For usage detail, consult the source of the plugin. III.III ColdSpring and Remoting
ColdSpring also provides a good foundation for exposing your application model to remote method calls. Currently, the primary way to do this is to write remote facades, which expose ColdSpring beans to remote calls by containing methods with access=remote. To see an example of a Service Layer, MVC framework integration (Mach-II and FuseBox 4) and an example of a Remote Faade, please refer to the ColdSpring sample app, located in the /examples directory within the ColdSpring source code.