|
|
 |
|
Depends: |
n/a |
Related: |
log |
|
|
|
Description
Inversion Of Control (IoC) is a well known design pattern that is generally used for configuring components'
dependecies. It is also know as the "Hollywood Principle - Don't Call Us We'll Call You" (introduced at
Mesa Programming Environment)
or, as more strictly defined, by
Martin Fowler - "Dependency Injection".
Despite the various definitions for IoC, in a nutshell, it can be described as follows:
Say we have two components A and B, and A depends on (uses) B. In a conventional
scenario (i.e. without IoC) A will need to explicitly obtain B and establish its dependecy on it.
Using an IoC design pattern, however, a system could automatically obtain and establish the dependency on B without
any explicit request on the part of component A - hence reversing (inverting) the control of dependency.
Following diagram illustrates this definition:
IoC is not the only way to make dependency control indirect.
The Service Locator
design pattern provides another way to decouple dependency control from a component (and not only in J2EE
applications). However, there is still a significant difference between IoC and Service Locator as Service Locator
requires an explicit request from the component to locate the dependency hence coupling component to a specific
Service Locator interface, whereas in IoC case no explicit interaction is required and no coupling is created.
There are several types of IoC implementations that are currently available today
(terminalogy from Martin Fowler):
- Type 1 (interface injection)
-
This type of IoC is based on providing a specific inteface for each specific instance of dependency injection
(i.e. for each component that needs that behavior). At runtime these interfaces will be analyzed and proper
dependencies will be set for each component. The major problem with this aproach is that it requires too many
"marker" interfaces to be created which leads to heavy maintaince requirements and a poluted design. The inteface injection type
of IoC is used by the open source Avalon framework. One of the
distinct capabilities of such an approach is that its implementation does not require an external, non-Java
configuration.
- Type 2 (setter injection)
-
In this type of IoC dependency injection is achived by setting certain properties using a JavaBean type of
convention. Usually, the specific properties are defined in an XML file and at startup or per request they are
set or "wired-up" to form a dependency. This is a most common type of IoC and used by variety of software
systems including some open source frameworks such as
Spring and
PicoContainer.
- Type 3 (constructor injection)
-
Unlike Type 1 and Type 2, Type 3 of IoC makes use of constructors to pass in a component's dependencies instead of
using setters or per-component injection interfaces. Although, seemingly an elegant solution that should, in theory,
lead to a better design by enforcing that each component is fully initialized during creation, the real
applicability of Type 3-only IoC is very limited. In fact, it goes against the very nature of IoC as it
basically requires each component to be designed to fit such an arrangement, which defeats the purpose of decoupling
dependency control from a component. Thus most of the IoC implementation use some combination of Type 3 and
Type 2 or Type 1. The PicoContainer provides an
attempt to develop an almost Type 3-only IoC system.
Top
IoC in xTier
xTier provides IoC functionality in two distinct ways:
-
Configuration for each service provided by xTier is based on IoC. Every XML configuration file uses the same
syntax and semantic to define IoC components that are used in the configuration of a particular service. See
XtierKernel for more information on services configuration.
-
xTier provides an 'ioc' service that exposes full IoC functionality and features to the developer
allowing the developre to create highly configurable and flexible software systems.
Note that in both cases the 'ioc' service and other configurable xTier services use exactly the same semantic
and configuration for defining IoC components (with one exception that is noted below).
xTier 'ioc' provides combination of traditional Type 2 and Type 3 types of IoC with some additional
unique features:
-
Support for 'new', 'singleton' and user-defined 'keyed' creation policies. The user defined "keyed" policy
allows, for example, creation of an IoC object per-thread by using the current thread as a key when creating
the IoC object.
-
Generalized support for setters and init methods. 'ioc' allows calling any arbitraty methods
on the object as long as its signature can be properly constructed. That feature is extremely important for
supporting "legacy" components that may require the constructor call, setting one or many parameters via
JavaBean type of setters or via other non-compliant methods and then calling one or more initialization
methods that may also take arbitrary input parameters. Furthermore, certain calls must be performed in a
certain order, repeated or intermixed with each other.
For example, 'ioc' can create an IoC object as a java.util.ArrayList
with certain size passed into constructor, populate it with certain pre-defined values via calls to
add(java.lang.Object) or add(int, java.lang.Object)
methods, call trimToSize() and pass this object to another IoC object as a
parameter.
Top
Configuration
The 'ioc' service acts as a factory for IoC objects. IoC objects are defined in the xtier_ioc.xml
configuration file. This file follows the standard xTier service configuration pattern and is illustrated by
the following complete example of IoC object definition:
| 1 |  | <!----> |
| 2 |  | <ioc uid="string" policy="new"> |
| 3 |  | <java class="java.lang.String"> |
| 4 |  | <ctor> |
| 5 |  | <arg type="string">xTier</arg> |
| 6 |  | </ctor> |
| 7 |  | </java> |
| 8 |  | </ioc> |
| 9 |  | |
| 10 |  | <!----> |
| 11 |  | <ioc uid="ref" ref-uid="string"/> |
| 12 |  | |
| 13 |  | <!-- |
| 14 |  | |
| 15 |  | |
| 16 |  | --> |
| 17 |  | <ioc uid="str.buf" policy="singleton"> |
| 18 |  | <java class="java.lang.StringBuffer"> |
| 19 |  | <ctor> |
| 20 |  | <arg ref-uid="ref"/> |
| 21 |  | </ctor> |
| 22 |  | </java> |
| 23 |  | </ioc> |
It is important to note that all following explanations about <ioc> XML tags equally apply to
any other xTier service that uses IoC functionality for its configuration or logic. Remember that the IoC semantic
and syntax are identical in all xTier services including 'ioc'. The 'ioc'service just directly exposes IoC-only
functionality to the end user.
The 'ioc' service configuration consists of one or more IoC descriptor definitions specified by the
<ioc> tag.
The formal specification for the <ioc> tags can be found in the xtier_dtd_includes.dtd DTD file
in the ${XTIER_ROOT}/config/dtd folder. <ioc> tag supports the following attributes:
| uid |
This attribute defines the Unique ID of the IoC descriptor. This attribute is mandatory.
|
| policy |
This attribute defines the creation policy for the IoC descriptor:
-
new - An IoC descriptor with this creation policy will create a new IoC object every time the
method makeIocObject(String) or makeIocObject(String, Object) is called.
-
singleton - An IoC descriptor with this creation policy will create a new IoC object only the
first time. All subsequent calls to the method makeIocObject(String) or
makeIocObject(String, Object) will return the same instance.
-
keyed - An IoC descriptor with this creation policy will create only once instance
for each key specified in the makeIocObject(String, Object) method call.
NOTE: this creation policy is only allowed in the 'ioc' service itself. All other xTier services can use the
'new' or 'singleton' creation policy but not the 'keyed'.
Note that this attribute cannnot be used together with the ref-uid attribute.
|
| ref-uid |
This attribute defines a referenced IoC descriptor. When ref-uid attribute is defined this
IoC descriptor basically acts as an alias for the referenced IoC descriptor. Note that this attribute
cannot be used together with the policy attribute.
|
The <ioc> tag consists of either the one <java> or one <clr> tags
that define how the IoC object should be created. There are two general ways how an IoC object can be created:
user can specify the name of the class and the instance of that class will be created, or user can specify the
other IoC descriptor and the method name that both would act as a factory for creating new IoC objects defined
this descriptor.
In first case user just specifies fully qualified class name in class attribute, and in the second case
user provides factory UID and method name via factory-uid and method attributes:
| class |
Fully qualified class name. Objects of this class (see also the <ctor> tag for
constructor definition) will be created when IoC descriptor creates new object. This attribute
cannot be mixed with factory-uid and method attributes.
|
| factory-uid |
This attribute defines UID of another IoC descriptor that will serve as a creation factory for
this IoC descriptor. Note that factory-uid and method attributes must always be
used together. Note also that these attributes cannot be used together with class attribue.
|
| method |
Name of the method that will be called on the object created by IoC descriptor referenced by
factory-uid attribute to create new IoC object. The mmethod must take no arguments and
return type java.lang.Object. Note that factory-uid and method attributes
must always be used together. Note also that these attributes cannot be used together with class
attribue.
|
<java> tag consists of an optional <ctor> tag defining the constructor call and zero
or more <call> tags defining method calls to be performed on the constructed object.
Each <ctor> and <call> tag consists of zero or more <arg> tags that
define the actual argument of the constructor or a method call. <ctor> tag has no attributes,
while <call> tag has one attribute method defining what method to call on the constructed
object. Note that if <ctor> tag is not specified the class is expected to have accessable no-arg
constructor.
<arg> tag has the following
attributes:
| null |
Value true or false indicates whether or not this argument is null.
The default value specified in DTD for this attribute is false.
|
| boxed |
Value true or false indicates whether or not this argument is a boxed type. For
example, if this attribute is true and the type attribute is int32 the
expected signature type for a constructor or method call will be java.lang.Integer.
The default value specified in DTD for this attribute is false.
|
| type |
This attribute specifies the type of the argument. The table below shows all supported types and their Java
counterparts in boxed and unboxed form:
| IoC Type |
Unboxed |
Boxed |
| int8 | byte | java.lang.Byte |
| int16 | short | java.lang.Short |
| int32 | int | java.lang.Integer |
| int64 | long | java.lang.Long |
| float32 | float | java.lang.Float |
| float64 | double | java.lang.Double |
| char | char | java.lang.Character |
| boolean | boolean | java.lang.Boolean |
| string | - | java.lang.String |
| date | - | java.util.Date |
|
| ref-uid |
This attribute specifies that the argument is a reference to another IoC descriptor whose value
will be used as a this argument's value. This attribute is the essence of establishing the dependency
by one IoC descriptor to another. Note that circular references are not allowed. ref-uid
attribute can only be used together with null attribute. If null attribute is set,
then null value be used.
|
| const |
This attribute allows to use static field in the class as an actual argument's value. The value of this
attribute
must be specified in the form className#fieldName, where className must be a
fully qualified class name.
Note that this attribute cannot be used with any other attributes in the <arg> tag.
|
Top
Examples
Usage of the 'ioc' service follows the standard pattern of using an xTier service: you need to obtain
an instance of the xTier kernel that serves as a service registry. Once you have the xTier kernel you can get
an instance of any service, in our case the 'ioc' service. Assume that we have the following IoC definition:
| 1 |  | <ioc policy="new" uid="str"> |
| 2 |  | <java class="java.lang.String"> |
| 3 |  | <ctor> |
| 4 |  | <arg type="string">xTier</arg> |
| 5 |  | </ctor> |
| 6 |  | </java> |
| 7 |  | </ioc> |
| 8 |  | |
| 9 |  | <ioc policy="keyed" uid="list"> |
| 10 |  | <java class="java.util.ArrayList"> |
| 11 |  | <ctor> |
| 12 |  | <arg type="int32">4</arg> |
| 13 |  | </ctor> |
| 14 |  | |
| 15 |  | <call method="add"><arg type="int32" |
| 16 |  | boxed="true">1</arg></call> |
| 17 |  | <call method="add"><arg type="int32" |
| 18 |  | boxed="true">2</arg></call> |
| 19 |  | <call method="add"><arg type="int32" |
| 20 |  | boxed="true">3</arg></call> |
| 21 |  | <call method="add"><arg ref-uid="str"/> |
| 22 |  | </java> |
| 23 |  | </ioc> |
With this configuration we can create a thread-local populated list using the following code:
| 1 |  | |
| 2 |  | XtierKernel xtier = XtierKernel.getInstance(); |
| 3 |  | |
| 4 |  | |
| 5 |  | IocService ioc = xtier.ioc(); |
| 6 |  | |
| 7 |  | |
| 8 |  | List list = (List)ioc.makeIocObject("list", |
| 9 |  | Thread.currentThread()); |
Download xTier for full examples and documentation.
Top
|