<< Back to Dashboard | << Interceptors Viewer
|
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):
Note for Applications Built Prior to ColdBox 3.0M6: Prior to ColdBox 3.0M6, the DSL supported a type="model" annotation for cfproperty tags. Going forward, you will need to use inject="model". This change was made to prevent the hijacking of the type attribute for the cfproperty tag.
// The following will not work in ColdBox 3+ <cfproperty name="FooBar" type="models:FooBar" /> // Change the "type" attribute to "inject" <cfproperty name="FooBar" inject="models:FooBar" />
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 |
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.
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.
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. |
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.
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>
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.
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>
So there you go. Simple as 1-2-3-4 well and 5 if you are guru status! Very simple and easy to use.