Interceptors:Autowire

last edited byusericonsullah on 22-Jul-2010

<< Back to Dashboard | << Interceptors Viewer

Contents

Autowire Interceptor

Introduction

The autowire interceptor is a great tool to use when using ColdBox's Model Integration, object factories like ColdSpring or LightWire or the ColdBox Cache. This interceptor will inject dependencies into the following ColdBox objects:

If you would like to autowire any other type of object on demand you can use the BeanFactory plugin to accomplish this. Just for your information, the autowire interceptor USES the BeanFactory plugin to do all of its dependency injections. You can see all of its methods by visiting the CFC API

What are dependencies? Dependencies are objects that are created by the ColdBox Model Integration, object factories, objects that have been placed into the ColdBox Cache or even application settings. Overall, object dependencies can be anything that the object at hand needs in order to operate. Thus, the goal of the autowire interceptor is to unobtrusively describe your object's dependencies and inject them for you. You can use the autowire interceptor to inject dependencies from various sources according to our Dependency DSL (Domain Specific Language):

Dependencies DSL

We have created a nice DSL for dependency injection that is used by using cfproperty and common setter methods. Not only does the cfproperty types apply to model objects but to anything that is autowired in ColdBox: plugins, handlers, interceptors, ioc produced beans, and on demand autowiring. Below is a chart of what you can use in both cfproperty tags and setter methods:

CFPROPERTY

Types DSL

Type Description
ioc Get the named ioc bean and inject it. Name comes from the cfproperty name or setter name
ioc:BeanName Get the ioc bean according to Bean Name in DSL
ocm Get the name key from the ColdBox cache and inject it. Name comes from the cfproperty name or argument name
ocm:ObjectKey Get the object from the ColdBox cache according to DSL object key.
model Get a model with the same name or alias as defined in the cfproperty name="{name}" attribute. Name comes from the cfproperty name or setter name
model:{name} Same as above but it will get the {name} model object from the DSL and inject it.
model:{name}:{method} Get the {name} model object, call the {method} and inject the results
webservice:{alias} Get a webservice object using an {alias} that matches in your coldbox.xml
coldbox Get the coldbox controller
coldbox:setting:{setting} Get the {setting} setting and inject it
coldbox:plugin:{plugin} Get the {plugin} plugin and inject it
coldbox:myPlugin:{MyPlugin} Get the {!MyPlugin} custom plugin and inject it
coldbox:datasource:{alias} Get the datasource bean according to {alias}
coldbox:configBean get the config bean object and inject it
coldbox:mailsettingsbean get the mail settings bean and inject it
coldbox:loaderService get the loader service
coldbox:requestService get the request service
coldbox:debuggerService get the debugger service
coldbox:pluginService get the plugin service
coldbox:handlerService get the handler service
coldbox:interceptorService get the interceptor service
coldbox:moduleService Get a reference to the ColdBox Module Service
coldbox:interceptor:{name} Get a reference of a named interceptor
coldbox:cacheManager get the cache manager
coldbox:fwConfigBean Get a configuration bean object with ColdBox settings instead of Application settings
coldbox:fwSetting:{setting} Get a setting from the ColdBox settings instead of the Application settings
entityService Inject a BaseORMService object for usage as a generic service layer
entityService:{entity} Inject a VirtualEntityService object for usage as a service layer based off the name of the entity passed in.
javaLoader:{class} Create an object from the JavaLoader plugin and its set of loaded java libraries
logbox Get a reference to the application's LogBox instance
logbox:root Get a reference to the root logger
logbox:logger:{category name} Get a reference to a named logger by its category name

cfproperty Dependencies

So if an object needs dependencies after creation (usually the case), then just use our good old friend cfproperty to demarcate or annotate what needs to be injected. The good thing again, is we just rely on unobtrusive metadata to define what needs to be injected and it can be documented!! Ahh how I love that! Below is a very complex example:

<cfcomponent name="MyHandler" output="false" extends="coldbox.system.eventhandler" autowire="true">

<---  Autowire Properties --->
<cfproperty name="myMailSettings" 	inject="ioc" 		scope="instance">
<cfproperty name="ColdBox" 		inject="coldbox" 	scope="instance">
<cfproperty name="ModelsPath" 		inject="coldbox:setting:ModelsPath" 	scope="instance">
<cfproperty name="Utilities" 		inject="coldbox:plugin:Utilities" 	scope="instance">
<cfproperty name="ConfigBean" 		inject="coldbox:configbean" 		scope="instance">
<cfproperty name="MailSettingsBean" 	inject="coldbox:mailsettingsBean" 	scope="instance">
<cfproperty name="MySiteDSN" 		inject="coldbox:datasource:mysite" 	scope="instance">
<cfproperty name="testModel" 		inject="model" 			        scope="instance">
<cfproperty name="initDate" 		inject="model:formBean:getinitDate" 	scope="instance">

</cfcomponent>

That is so nice. We can use this DSL to inject almost anything into our objects. This approach of injection by annotation is our prefered method of dependency injection due to the fact that we can easily document it, easily see the dependencies once we open our components and it is easy to maintain. Below we learn about our counterparts, setter injection.

Setter Dependencies

You can easily use the mentioned DSL to wire up objects via setter injection instead of cfproperty injections. All you have to do is place a marker in the setter function in order to describe the dependency. The default attribute is called: _wireme. But what is setter injection? Setter injection is a way to inject objects/data to your desired/target objects by means of creating a setter method. A setter method is a simple method that starts with the word set. Below are some examples:

setUserService()
setManager()
setSetting()
setConfigBean()

As you can see from above, they all start with the word set and then they have a name. This name is usually the name of the object that you want to inject or set in your target object. This interceptor will first get a list of all the methods in the target object that start with set and try to find matches according to our DSL or defaults. So a simple example would be the following:

<---  Setter Markers --->
<cffunction name="setTransfer" type="transfer.com.Transfer" output="true" _wireme="ocm">

</cffunction>

<---  Setter Markers --->
<cffunction name="setDSN" type="any" output="true" _wireme="coldbox:datasource:MyDSN">

</cffunction>

As you can see, you use the setter function marker: _wireme to tell the bean factory how to wire up the argument or setter method. Now, if you do not like my default marker, then choose your own. Just create a new setting in your coldbox.xml.cfm named: beanfactory_dslMarker.

<Setting name="beanFactory_dslMarker" value="wireit" />

//Then use the wireit marker:
<---  Setter Markers --->
<cffunction name="setDSN" type="any" output="true" wireit="coldbox:datasource:MyDSN">

</cffunction>

As you can see from the samples above, wiring up setter methods is fairly easy and very descriptive. You are also not relying on inherited functionality or conflicting code, it is purely metadata that can be ignored if using another factory other than ColdBox. Other interesting aspects of setter injections are the following:

Now that we have learned about all the dependency injections and DSL we can move on and see how to configure our interceptor.

1. Configure the interceptor

The way to configure the autowire interceptor is via your configuration file. Look below for the sample:

<Interceptor class="coldbox.system.interceptors.autowire">
  <Property name="debugMode">true</Property>
  <Property name="completeDIMethodName">onDIComplete</Property>
  <Property name="enableSetterInjection">true</Property>
</Interceptor> 

That's it!! So let's go over it. We first declare the autowire interceptor and we set some of the properties discussed below.

Property Type Default Required Description
debugMode boolean false false Whether to use the logging facilities on successful/failed injections.
completeDIMethodName string onDiComplete false This takes in the name of a method to fire on the injected object after all dependencies have been injected. This is incredible useful when you need to configure an object right after injections. It uses the onDiComplete convention, but you can use your own.
enableSetterInjection boolean true false Determines whether you will use setter injection alongside cfproperty metadata injections or not.

2. autowire metadata

The next step is to add a new property to the cfcomponent tag of the objects you would like to have auto wiring turned on. Simple as that.

autowire={boolean}

<cfcomponent name="handler" output="false" autowire="true"> 

This simple property tells the framework that this object (plugin,interceptor or handler) needs to have dependencies injected into it. Please note that if you use the bean factory for on demand autowiring, you can disable the check for this metadata argument.

3. Metadata overrides

You can override two autowiring properties on a per-object basis by creating two attributes in your cfcomponent tag:

Version 2.6.2 and above only

<cfcomponent name="handler" 
             output="false" 
             autowire="true" 
             autowire_stopRecursion="transfer.com.TransferDecorator"
             autowire_setterInjection="false">

 
</cfcomponent>

4. Dependencies

Now that you have declared that the component will be autowired, its time to create or describe which dependencies this object needs. We discovered our dependency DSL above so let's use it.

<cfproperty name="UserService"   type="ioc" scope="instance" />
<cfproperty name="LookupService" type="ioc" scope="instance" />
<cfproperty name="PublicService" type="ocm" scope="this" />


//Setter methods
<cffunction name="setuserService" access="private" returntype="void" output="false" _wireme="ioc">
	<cfargument name="userService" type="mypath.model.userService" required="true">
	<cfset instance.userService = arguments.userService />
</cffunction>

So to recap, we declared the autowire interceptor in our configuration file, we added the autowire metadata to our cfcomponent tag and we added our cfproperty tags or our setter methods. An important aspect to notice is that the setter method has an access of private. You can leave this as private and the autowire interceptor will still be able to use it. Well, you can be done if you want, the last step is optional.

5. Complete DI Method Name

This step is completely optional and only used if you declared it in your config file and you created the appropriate method on your object. What this feature means, is that you can create a method on your object that has the same name as you declared it in your config. The ColdBox default is onDIComplete. So if this method is detected in your object, then ColdBox will execute it AFTER all dependencies have been injected in to the object. This is great for configuring an object once we know all dependencies have been injected.

<cffunction name="onDIComplete" access="public" returntype="void" output="false">
	<---  Call Anything to configure this object --->
	<cfset setRoles( getUserService().getAllRoles() )>
	<cfset validateAllRoles()>
</cffunction>

Conclusion

So there you go. Simple as 1-2-3-4 well and 5 if you are guru status! Very simple and easy to use.

 
Download in other Formats:
markup Markup | pdf PDF | swf SWF | html HTML | word Word

comments Comments (0)