Changes between Initial Version and Version 1 of HowTos/HowToUseBeanInjection

Show
Ignore:
Timestamp:
05/04/09 10:05:52 (17 years ago)
Author:
davidhenry@… (IP: 174.99.26.254)
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • HowTos/HowToUseBeanInjection

    v1 v1  
     1= How To Use Bean Injection = 
     2 
     3One 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. 
     4 
     5In 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: 
     6 
     7{{{ 
     8<cffunction name="TranslatePhrase" access="public" returntype="void" output="false"> 
     9    <cfargument name="event" type="any"> 
     10 
     11    <cfset var translator = createObject("component", "translator.model.PigLatinTranslator").init("aeiou") /> 
     12    <cfset var phrase = arguments.event.getValue("phrase") /> 
     13    <cfset var result = translator.translate(phrase) /> 
     14 
     15    <cfset arguments.event.trace("TranslatePhrase Results", result) /> 
     16    <cfset arguments.event.setValue("translatedPhrase", result) /> 
     17</cffunction> 
     18}}} 
     19 
     20In this case, we were creating the object directly using ColdFusion's createObject() function: 
     21{{{ 
     22<cfset var translator = createObject("component", "translator.model.PigLatinTranslator").init("aeiou") /> 
     23}}} 
     24 
     25In 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): 
     26 
     27{{{ 
     28<!-- Put definitions for your own beans and services here --> 
     29}}} 
     30 
     31So we will place our new bean definition immediately below this: 
     32 
     33{{{ 
     34<!-- Put definitions for your own beans and services here --> 
     35<bean id="translator" class="translator.model.PigLatinTranslator"> 
     36    <constructor-arg name="vowels"><value>aeiou</value></constructor-arg> 
     37</bean> 
     38}}} 
     39Without getting into too much detail, there are a couple of things to note here: 
     40 
     41First, 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. 
     42 
     43Once 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. 
     44 
     45The 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: 
     46{{{ 
     47<controller name="MyController" type="translator.controller.Controller" beans="translator"> 
     48    <message-listener message="OnRequestStart" function="OnRequestStart" /> 
     49    <message-listener message="OnRequestEnd" function="OnRequestEnd" /> 
     50    <message-listener message="NeedTranslation" function="TranslatePhrase" /> 
     51</controller> 
     52}}} 
     53Note 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. 
     54 
     55The second method would be to instead add the "beans" attribute directly to the controller's cfccomponent tag: 
     56{{{ 
     57<cfcomponent output="false" hint="I am a Model-Glue controller." extends="ModelGlue.gesture.controller.Controller" beans="translator"> 
     58}}} 
     59This 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: 
     60{{{ 
     61<cffunction name="TranslatePhrase" access="public" returntype="void" output="false"> 
     62    <cfargument name="event" type="any"> 
     63 
     64    <cfset var phrase = arguments.event.getValue("phrase") /> 
     65    <cfset var result = beans.translator.translate(phrase) /> 
     66 
     67    <cfset arguments.event.trace("TranslatePhrase Results", result) /> 
     68    <cfset arguments.event.setValue("translatedPhrase", result) /> 
     69</cffunction> 
     70}}} 
     71So 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. Note that it is not necessary to set the object into a function-local (var-scoped) variable, as we could also refer to the object in the beans scope directly, if desired: 
     72 
     73And 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. 
     74Switching to Bean Injection in an Upgraded Model-Glue 2 Application 
     75If 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. 
     76 
     77For anyone who is unfamiliar with either of these concepts, here is a quick summary: 
     78=== getModelGlue().getBean() === 
     79 
     80The 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: 
     81{{{ 
     82<cfset var translator = getModelGlue().getBean("translator") /> 
     83}}} 
     84 
     85Note 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. 
     86 
     87 
     88If 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: 
     89{{{ 
     90<cffunction name="init" access="public" output="false" hint="Constructor"> 
     91    <cfargument name="framework" /> 
     92 
     93    <cfset super.init(framework) /> 
     94 
     95    <cfreturn this /> 
     96</cffunction> 
     97}}} 
     98 
     99=== Autowiring === 
     100 
     101Autowiring 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: 
     102 
     103{{{ 
     104<cffunction name="setTranslator" access="public" returntype="void" output="false"> 
     105    <cfargument name="translator" type="any" required="true" /> 
     106 
     107    <cfset variables.instance.translator = arguments.translator /> 
     108</cffunction> 
     109}}} 
     110 
     111And then the object can be accessed via the specified variable name: variables.instance.translator, in this case. 
     112 
     113=== Encapsulating Model Access in Getter Methods === 
     114 
     115It 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: 
     116 
     117{{{ 
     118<cffunction name="getTranslator" access="public" returntype="any" output="false"> 
     119    <cfreturn variables.instance.translator /> 
     120</cffunction> 
     121}}} 
     122 
     123This 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). 
     124 
     125 
     126The 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. 
     127 
     128=== Upgrading to Bean Injection === 
     129 
     130In 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: 
     131 
     132{{{ 
     133<cfset getModelGlue().getBean("translator").translate(phrase) /> 
     134}}} 
     135 
     136To this: 
     137 
     138{{{ 
     139<cfset beans.translator.translate(phrase) /> 
     140}}} 
     141 
     142If 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: 
     143 
     144{{{ 
     145<cfset variables.instance.translator.translate(phrase) /> 
     146}}} 
     147 
     148To this: 
     149 
     150{{{ 
     151<cfset beans.translator.translate(phrase) /> 
     152}}} 
     153 
     154And 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: 
     155 
     156{{{ 
     157<cffunction name="getTranslator" access="public" returntype="any" output="false"> 
     158    <cfreturn variables.instance.translator /> 
     159</cffunction> 
     160}}} 
     161 
     162To this: 
     163 
     164{{{ 
     165<cffunction name="getTranslator" access="public" returntype="any" output="false"> 
     166    <cfreturn beans.translator /> 
     167</cffunction> 
     168}}} 
     169 
     170And the individual references to the object via the getTranslator() method would remain unchanged. 
     171 
     172