Bean Injection
One of the time-saving new features of Model-Glue:Gesture is the capability to automatically inject model components that are managed by ColdSpring into the application's controllers. The term "bean injection" is used to describe this feature, as this is the ColdSpring term for a managed object (meaning that the object is created and cached by ColdSpring, and one "asks" ColdSpring for the object instead of instantiating it directly). A full explanation of ColdSpring is beyond the scope of this document, so examples of ColdSpring usage are intentionally kept extremely simple -- please refer to www.coldspringframework.org for more information.
In order to see how bean injection works, we will use the example model component from the Quickstart guide, PigLatinTranslator.cfc. In the Quickstart, we referenced this object in the TranslatePhrase function in our controller:
<cffunction name="TranslatePhrase" access="public" returntype="void" output="false">
<cfargument name="event" type="any">
<cfset var translator = createObject("component", "translator.model.PigLatinTranslator").init("aeiou") />
<cfset var phrase = arguments.event.getValue("phrase") />
<cfset var result = translator.translate(phrase) />
<cfset arguments.event.addTraceStatement("TranslatePhrase Results", result) />
<cfset arguments.event.setValue("translatedPhrase", result) />
</cffunction>
In this case, we were creating the object directly using ColdFusion's createObject() function:
<cfset var translator = createObject("component", "translator.model.PigLatinTranslator").init("aeiou") />
In order to use bean injection instead, we would first need to add this object to the ColdSpring.xml configuration file. There is a comment in this file that indicates where to do so (although this is just a suggestion, not a requirement):
<!-- Put definitions for your own beans and services here -->
So we will place our new bean definition immediately below this:
<!-- Put definitions for your own beans and services here -->
<bean id="translator" class="translator.model.PigLatinTranslator">
<constructor-arg name="vowels"><value>aeiou</value></constructor-arg>
</bean>
Without getting into too much detail, there are a couple of things to note here:
First, we have used a bean id of "translator", which we will use to refer to the object within the beans scope. Next, the bean class is identical to the full CFC path used in the previous createObject() method. And finally, the argument passed into the init() method when creating the object is instead specified in the ColdSpring.xml file via the constructor-arg tag. For a more detailed explanation of ColdSpring's configuration syntax, please refer the the ColdSpring project site, and particularly the ColdSpring Quickstart guide.
Once the bean (object) has been defined in ColdSpring.xml, then it will be available for injection after a reload of the Model-Glue framework. There are actually two different ways that we can do this.
The first method is to add the bean id to the new, optional "beans" attribute of the controller tag in the ModelGlue.xml file. To see this in action, let's use the controller XML from the Quickstart guide:
<controller name="MyController" type="translator.controller.Controller" beans="translator">
<message-listener message="OnRequestStart" function="OnRequestStart" />
<message-listener message="OnRequestEnd" function="OnRequestEnd" />
<message-listener message="NeedTranslation" function="TranslatePhrase" />
</controller>
Note the addition of the "beans" attribute, with value of "translator", which is the id of the bean in ColdSpring.xml. In this case we are only injecting a single object, but multiple objects can be injected by supplying a comma-delimited list of bean ids.
The second method would be to instead add the "beans" attribute directly to the controller's cfcomponent tag:
<cfcomponent output="false" hint="I am a Model-Glue controller." extends="ModelGlue.gesture.controller.Controller" beans="translator">
This has the exact same result, so we can choose whichever option appeals to us. In either case, objects that are injected are subsequently available to Model-Glue controllers via the new "beans" scope, so we can refer to our model object with the reference "beans.translator". Here is the controller function from the Quickstart with the necessary change:
<cffunction name="TranslatePhrase" access="public" returntype="void" output="false">
<cfargument name="event" type="any">
<cfset var phrase = arguments.event.getValue("phrase") />
<cfset var result = beans.translator.translate(phrase) />
<cfset arguments.event.addTraceStatement("TranslatePhrase Results", result) />
<cfset arguments.event.setValue("translatedPhrase", result) />
</cffunction>
So in this case, the object creation is handled by ColdSpring, and we are given a reference to the object to work with in our controller. And that's it! Once your model objects are defined in ColdSpring, you can access them in any Model-Glue controller simply by adding the beans attribute and calling them via the beans scope.
Switching to Bean Injection in an Upgraded Model-Glue 2 Application
If you are already using ColdSpring-managed beans in your Model-Glue:Unity application, then you are most likely using one of two existing methodologies to access them in your controllers: the getBean() method or autowiring. Note that both of these approaches are still supported in Model-Glue:Gesture, so you do not have to change anything in order to upgrade -- it will continue to work without needing any alterations.
For anyone who is unfamiliar with either of these concepts, here is a quick summary:
getModelGlue().getBean()
The older of the two previous options is the getModelGlue().getBean() method. This function is available in the Model-Glue framework API, and can be called anywhere in a Model-Glue controller, passing in the bean id as the single argument:
<cfset var translator = getModelGlue().getBean("translator") />
Note that this would either require that the getBean() call be used in every place that a model object is needed, or that an init() method be defined for the controller component in order to set the model objects into the controller's private variables scope for use in other methods.
If an init() method is defined in a controller, it is also necessary to call super.init(), passing in the argument that was passed into the method by the framework. As an example, here is the init() method demonstrated in the application template:
<cffunction name="init" access="public" output="false" hint="Constructor">
<cfargument name="framework" />
<cfset super.init(framework) />
<cfreturn this />
</cffunction>
Autowiring
Autowiring added a layer of abstraction, whereby instead of calling a model object directly, one would instead define a setter method in the controller that follows a naming convention of "set[bean id]", and Model-Glue will "autowire" the controller with any matching beans in the ColdSpring definition that have a matching id:
<cffunction name="setTranslator" access="public" returntype="void" output="false">
<cfargument name="translator" type="any" required="true" />
<cfset variables.instance.translator = arguments.translator />
</cffunction>
And then the object can be accessed via the specified variable name: variables.instance.translator, in this case.
Encapsulating Model Access in Getter Methods
It is worth mentioning that a common practice frequently employed in conjunction with autowiring setters is the use of corresponding getter methods to encapsulate access to the model objects, rather than referring to them directly in the variables scope:
<cffunction name="getTranslator" access="public" returntype="any" output="false">
<cfreturn variables.instance.translator />
</cffunction>
This technique has both benefits and drawbacks. On the positive side, if any additional logic needs to be run when accessing a model object, this mechanism allows for that logic to be defined in one place and then leveraged everywhere. It also means that a change to the storage location for the object can be made without altering any of the calling code (as we will see in a moment).
The potentially negative aspect is the additional overhead that is incurred both in object instantiation, as a CFC with more methods will take longer to create, and in the method calls themselves, as a variable reference is faster than a function call. Although each person will need to weigh the implications of this trade-off in their own applications, the performance penalties in question are almost certainly negligible in comparison to other likely bottlenecks such as unoptimized database queries.
Upgrading to Bean Injection
In order to switch to using bean injection in place of getBean() method calls, the changes required will depend upon the manner in which the application has been structured. If model components are referenced inline in the controller code every time they are needed, then each instance would be replaced. As an example, we would change this:
<cfset getModelGlue().getBean("translator").translate(phrase) />
To this:
<cfset beans.translator.translate(phrase) />
If autowiring is being used, then the autowiring setters would be removed, and each individual reference to the private variable storing the model component would be changed. In this example, we would change this:
<cfset variables.instance.translator.translate(phrase) />
To this:
<cfset beans.translator.translate(phrase) />
And if getters are being used to encapsulate access to the object, then the change would only need to be made in the getter method itself, from this:
<cffunction name="getTranslator" access="public" returntype="any" output="false">
<cfreturn variables.instance.translator />
</cffunction>
To this:
<cffunction name="getTranslator" access="public" returntype="any" output="false">
<cfreturn beans.translator />
</cffunction>
And the individual references to the object via the getTranslator() method would remain unchanged.
![(please configure the [header_logo] section in trac.ini)](/ModelGlue.com/trac.cgi/chrome/site/your_project_logo.png)