Interceptors:Autowire
<< Back to Dashboard | << Interceptors Viewer
|
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:
- Plugins
- Handlers
- Interceptors
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
- name : The name of the property to be injected
- type : The type of property to inject using our DSL (see chart below)
- scope : Into which scope to inject (Default is variables scope)
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:
- Your setter method access types can be private if you so desire it. The interceptor and bean factory will now how to go around this. Remember that objects are shy, if you don't want it exposed, then set it as private.
- If you do not use a marker then the bean factory will do the following:
- Determine if you are using ColdSpring or LightWire?
- Yes, then the default type is ioc
- No, then the default type is model or model integration.
- Determine if you are using ColdSpring or LightWire?
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:
- autowire_stopRecursion : The class path of when to stop recursion for this particular object.
- autowire_setterInjection : Whether to use setter injection or disable it for this particular object.
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.
SideBar
User Login
Comments (