Context Standard Plugin: Neodymium
Dependency injection a.k.a., Inversion of Control has been a first-class citizen in the world of enterprise application development for some time now. Neodymium is a context plugin that provides an intuitive annotations-based, high-performance object wiring framework which provides all of the rich benefits of a full-featured dependency injection library.
Automatic Dependency Injection
Neodymium is an injection provider evolved from the QuickSilver framework. Except Neodymium does not have any 3rd-party library dependencies to perform the injection tasks. Just like the QuickSilver approach, Neodymium utilizes the ever-popular convention over configuration paradigm to ensure that wiring your context objects is simple and straight-forward. Neodymium does not reuqire that you provide a class-by-class definition or configuration indicating which objects need to be injected into wich other objects. As long as you follow the conventions and propertly utilize the wiring annotations - you can ensure that dependencies will be available on objects as needed.
Autowired Context in 30 Seconds
Before we get into the details of Neodymium, I'll show you just how easy it is to create an autowired context;
Of course the resulting context above will be re-created on every page request, so cache the finalized Context as you see fit. (Contexts need only be initialized once during the lifetime of the application that utilizes it - I can't stress that enough).
OR for contexts configured with an XML file;
Done! Now let's dive into the details.
Wire-by-property-type Approach
The first and most important convention of this plugin is the wire-by-property-type approach. When creating a Context that utilizes the NeodymiumPlugin plugin, any CFC within the context that has a declared property will be a candidate for automatic dependency injection. The property declaration or <cfproperty /> tag within a CFC is a part of the standard CFML API, as is it's type attribute. The combination of the declaration and the type are all you need in order to indicate that injection needs to occur. For example;
Script Example 1; Person.cfc
Script Example 2; Person.cfc
Script Example 3; Person.cfc
Tag Example; Person.cfc
Each of the above examples of CFC's are both logically and functionally equivalent and use only native CF API constructs. Also, each has declared a property that is a candidate for automatic dependency injection - the address property.
Only Custom Types are Wiring Candidates
Only custom data-types (custom CFC's) will be considered when Neodymium introspects the context to perform automatic dependency injection. Any native CF data-types such as "string", "uuid", "numeric", "boolean", "xml"... etc are effectively ignored. If a custom type is encountered, then an instance of that type is created and injected into the parent component via a "setter" method whose name is the same as the property name.
In our above example, when an instance of com.mydomain.Person is requested from the context via context.getObjectInstance("com.mydomain.Person"), an instance of com.mydomain.Address is created (along with any of it's own dependencies) and is injected into the resulting instance of Person by invoking a setter on Person called setAddress() - which corresponds to the name of the property.
Make sure accessors="true"
In order to ensure that Neodymium has access to the correct object setter for the autowired property, ensure that you've declared accessors="true" on your CFC. This will automatically provide both a getter and a setter for the property requiring an injected dependency.
Singletons
Along with dependency injection, Neodymium allows you to manage your context objects as singletons with a simple delcarative annotation; @cfcommons:ioc:singleton. This annotation does not require an associated value when working in a script-based CFC, it simply acts as a flag instructing Neodymium to manage one and only one instance of the flagged CFC during the life of the context itself. When working with a tag-based CFC, simply set the value to "true".
Script Singleton
Tag Singleton
Specifying Concrete Types for Interface-Specific Property Types
For those of you interested in adhering to interface based programming techniques (which I highly recommend) you may already be wondering how Neodymium handles dependency injection when a property specifies a type of interface. Of course, we can't create an object from an interface in order to perform the injection, we need a concrete implementation of that interface. This is easily accomplished by providing the @cfcommons:ioc:implementation annotation along with the property type. In these cases, the setter type and getter return type for your automatically generated mutators remain interface-based, but Neodymium will use the class specified by the implementation annotation for injection purposes;
Script Implementation Annotation
Tag Implementation Annotation
Excluding Custom Property Types from Wiring
There will likely be occasions when you do not want a property injected, even when it's declared type is a custom CFC. You can exclude any property from being automatically wired by simply annotating that property with the @cfcommons:ioc:exclude annotation;
Script Exlude Annotation
Tag Exclude Annotation
Init() Arguments
Neodymium also allows you to provide initialization arguments that will be passed into your CFC's init() method when an instance of the class is created. This is done by annotating your CFC with the @cfcommons:ioc:initArgs annotation. It's value must be formatted as an implicit structure, with it's keys mapping directly to init() method arguments;
Script initArgs Annotation
Tag initArgs Annotation
Overriding the Default Annotations
Each annotation that we've detailed so far can be effectively replaced with your own, as you see fit. You simply need to tell Neodymium about it when you initialize the plugin. For instance, if you wanted to use the annotation myAwesomeSingleton to flag CFC's as singletons, you would initialize the NeodymiumPlugin like this;
Now annotating the actual CFC would look like this;
Each annotation can be overriden with an optional NeodymiumPlugin init argument. Below is the method signature of the Neodymium Plugin constructor, detailing the annotation markers that you can override to fully customize the way your context is auto-wired;
Overriding Annotation Markers via XML
Of course, all context can be configured by an xml file. If you wanted to auto-wire your HTTP context for instance AND you wanted to customize the annotation markers, your plugin declaration within the context-config.xml file might look like this;
For more information on how to configure your context declaratively with one or more plugins - including this one, check out the documentation here .