In this chapter, we will explore the attributes that are the essential components for declaring Object Definitions with Spring CodeConfig. These attributes have a corresponding representation in Spring XML so should be very familiar to current Spring.NET users.
The [Configuration]
attribute is applied to classes that contain one or more [ObjectDef]
attributed-methods. During scanning by the
CodeConfigApplicationContext
, only types having the
[Configuration]
attribute will be considered candidates to contain Object Definition
configurations.
The most common usage of the [Configuration]
attribute simply applies the attribute to a class without any
attribute parameters. The name of the of the type, once registered
with the CodeConfigApplicationContext
, will be the
name of the class itself. Note that it is not a common use-case to ask
the container for this configuration class.
[Configuration] public class MyConfigurationClass { //[ObjectDef] methods here }
While not a common use-case, if you need fine-grained control
over the name of the type registered with the
CodeConfigApplicationContext
, the [Configuration]
attribute accepts a Name
parameter which will be
applied to the registered Object Definition in the
CodeConfigApplicationContext
.
[Configuration("MySpecialConfigurationClass")] public class MyConfigurationClass { //[ObjectDef] methods here }
The [ObjectDef]
attribute is applied to one or more methods within any class to which the
[Configuration]
attribute has been applied. During scanning, any public
virtual
method having the [ObjectDef]
attribute is considered to contain Object Definition metadata.
The first usage of the [ObjectDef]
attribute simply applies the attribute to the method. The name of the
of the Object to be registered with the
CodeConfigApplicationContext
, will be the name of
the method itself.
[Configuration] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } }
If you need to control the name of the Object registered with
the CodeConfigApplicationContext
, the
[ObjectDef]
attribute accepts one or more comma-delimited names as aliases for the
Object when registered.
[Configuration] public class MyConfigurationClass { [ObjectDef(Names="TheHomeController")] public virtual HomeController HomeController() { return new HomeController(); } [ObjectDef(Names="TheSpecialNameForAboutController,AliasForAboutController")] public virtual AboutController AboutController() { return new AboutController(); } }
If you need to declare an Initialization Method for the object
definition, the [ObjectDef]
attribute accepts a method name to be invoked at the appropriate stage
in the object's creation by the
CodeConfigApplicationContext
.
[Configuration] public class MyConfigurationClass { [ObjectDef(InitMethod="AfterCreation")] //assumes a public method on the HomeController type named 'AfterCreation()' public virtual HomeController HomeController() { return new HomeController(); } }
Note | |
---|---|
You can just also call the method 'AfterCreation' inside the body of the HomeController implementation itself. |
If you need to declare a Destroy Method for the object
definition, the [ObjectDef]
attribute accepts a method name to be invoked at the appropriate stage
in the object's destruction by the
CodeConfigApplicationContext
.
[Configuration] public class MyConfigurationClass { [ObjectDef(DestroyMethod="CleanupResources")] //assumes a public method on the HomeController type named 'CleanupResources()' public virtual HomeController HomeController() { return new HomeController(); } }
The [DependsOn]
attribute can be applied to any
[ObjectDef]
-attributed
method to declare a construction sequence dependency upon one or more
objects that the CodeConfigApplicationContext
will
ensure are created prior to the creation of the current object. This is
only required if there is a hidden depedency between the two classes that
isn't exposed via constructor or setter properties.
The [DependsOn]
attribute accepts one or more
comma-delimited strings representing the names of the objects upon
which the current object will depend.
[Configuration] public class MyConfigurationClass { [ObjectDef] [DependsOn("Dependency1")] //HomeController requires "Dependency1" to be created first public virtual HomeController HomeController() { return new HomeController(); } [ObjectDef] [DependsOn("Dependency1", "Dependency2")] //AboutController requires "Dependency1" and "Dependency2" to be created first public virtual AboutController AboutController() { return new AboutController(); }b }
The [Import]
attribute allows you to identify one
or more additional types that will also be scanned when the current type
is scanned.
To support your specifying additional types to scan, the
[Import]
attribute accepts one or more
comman-delimited Types to scan as well. To be candidates for scanning,
the types listed in this array must also have the [Configuration]
attribute applied to them. Note that no error is reported if these
types lack the[Configuration
] attribute, but
without it they will not satisfy the scanner's requirements for
Configuration candidates.
[Configuration] [Import(typeof(MySecondConfiguration), typeof(MyThirdConfiguration))] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } } [Configuration] public class MySecondConfiguration { [ObjectDef] public virtual AboutController AboutController() { return new AboutController(); } } [Configuration] public class MyThirdConfiguration { [ObjectDef] public virtual SomeOtherController SomeOtherController() { return new SomeOtherController(); } }
Types pointed to by one [Import]
attribute
may in turn have their own [Import]
attributes
pointing to yet more types to scan. Using this approach, its possible
to specify perhaps only a single 'root' class from which to 'begin'
the scan and leverage the [Import]
attribute to
'chain' successive types into the scanning scope, transitively
pointing from one [Configuration]
-attributed
type to the next as in the following example where
MyConfigurationClass
has an
[Import]
attribute pointing to the
MySecondConfiguration
class which in turn has an
[Import
] attribute pointing to the
MyThirdConfiguration
class.
[Configuration] [Import(typeof(MySecondConfiguration))] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } } [Configuration] [Import(typeof(MyThirdConfiguration))] public class MySecondConfiguration { [ObjectDef] public virtual AboutController AboutController() { return new AboutController(); } } [Configuration] public class MyThirdConfiguration { [ObjectDef] public virtual SomeOtherController SomeOtherController() { return new SomeOtherController(); } }
Given this series of transitive or 'chained'
[Import]
attributes, the
CodeConfigApplicationContext
would only need to be
told to scan the single MyConfigurationClass
type
in order to effectively scan and discover the [ObjectDef]
methods contained all three [Configuration]
types. Using this approach, its possible to use a compositional
approach to segregate [ObjectDef]
methods into multiple [Configuration]
classes and then chain them together just as one might do with XML
based configuration files for many of the other
IApplicationContext
implementations.
Just as the [Import]
attribute provides the
ability to reference and import additional types attributed with the
[Configuration]
attribute, the [ImportResrource]
attribute
permits referencing and importing Object Defintions from any
IResource
implementation including those defined
natively in Spring.NET ("file://
",
"assembly://
", etc.).
To import an IResource
, simply reference its
path in the [ImportResource]
attribute. In the
following example, the embedded assembly resource
ObjectDefinitions.xml
is being imported into the
process of scanning and parsing the
MyConfigurationClass
type. This makes all of the
object definitions present in the embedded
ObjectDefinitions.xml
file available to the
CodeConfigApplicationContext
as it builds its
Object Defintions.
[Configuration] [ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } }
To import multiple IResource
s, simply provide
multiple [ImportResource]
attributes, each with the
single resource to import. The following example demonstrates
importing an embedded assembly resource as well as two XML files on
disk.
[Configuration] [ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.xml")] [ImportResource("file://ServiceObjectDefinitions.xml")] [ImportResource("file://c:/MySpecialConfigLocation/Deployment/SiteB/RepositoryObjectDefinitions.xml")] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } }
By default, the [ImportResource]
attribute
will use the Spring.NET provided
XmlObjectDefinitionReader
to parse the imported
IResource
. If your imported resource cannot be
parsed with the XmlObjectDefinitionReader
then you
can provide the type of the specific implementation of
IObjectDefinitionReader
that the
[ImportResource]
process should use. Note that this provided
type must implement the IObjectDefinitionReader
interface or an Exception will be thrown when the
[ImportResource]
attribute is evaluated.
[Configuration] [ImportResource("assembly://MyApplication.Config/MyCompany.MyApplication.Config/ObjectDefinitions.CSV", DefinitionReader = typeof(MyCommaSeparatedValueObjectDefinitionReader))] public class MyConfigurationClass { [ObjectDef] public virtual HomeController HomeController() { return new HomeController(); } }
The [Lazy]
attribute allows you to specify that
the object described by the [ObjectDef]
should either be be lazily or eagerly instantiated.
To specify Lazy Instantiation of any singleton object by the
CodeConfigApplicationContext
, apply the
[Lazy]
attribute to any [ObjectDef]
-attributed
method as in the following example. Note that the default usage of
the[Lazy]
attribute sets Lazy = true and so
[Lazy]
and [Lazy(true)]
are
considered functionally equivalent. Also note that specification of
lazy instantiation is only valid for Singleton-scoped objects; any
attempt to apply the [Lazy]
attribute to a
non-singleton-scoped [ObjectDef]
method will result in an exception thrown by the underlying
ApplicationContext.
[Configuration] public class MyConfigurationClass { [ObjectDef] [Lazy] //functionally equivalent to [Lazy(true)] public virtual HomeController HomeController() { return new HomeController(); } [ObjectDef] [Lazy] //invalid here because the scope is non-Singleton [Scope(ObjectScope.Prototype)] public virtual InvalidController InvalidController() { return new InvalidController(); } }
As the default instantiation behavior for the
CodeConfigApplicationContext
is to perform eager
instantiation, no special attribute needs to be applied to achieve
eager instantiation of the object. However, the
[Lazy]
attribute will accept a
bool
value of false
if you
desire to be explicit about the [Lazy]
setting for
the [ObjectDef]
as shown in the following code snippet.
[Configuration] public class MyConfigurationClass { [ObjectDef] [Lazy(false)] //functionally equivalent to simply not applying the [Lazy] attribute at all public virtual HomeController HomeController() { return new HomeController(); } }
The [Scope] attribute permits you to declare the lifetime of the
object managed by the CodeConfigApplicationContext
for
each [ObjectDef]
-attributed
method.
The [Scope]
attribute accepts a single
ObjectScope
enum
argument as
defined by Spring.NET and demonstrated in the following snippet:
[Configuration] public class MyConfigurationClass { [ObjectDef] [Scope(ObjectScope.Prototype)] public virtual HomeController HomeController() { return new HomeController(); } [ObjectDef] [Scope(ObjectScope.Singleton)] //technically redundant since all types default to Singleton ObjectScope public virtual CustomerRepository CustomerRepository() { return new CustomerRepository(); } [ObjectDef] [Scope(ObjectScope.Session)] public virtual UserSettings UserSettings() { return new UserSettings(); } }