-
Notifications
You must be signed in to change notification settings - Fork 0
LifeCycle
#Object Lifecycle
This chapter provides details about various aspects of the lifecycle of objects managed by Parsley.
##About Managed Objects
There are many ways to instruct the framework to manage a particular object. But it is important to understand that all these various options are explicit and there is no magic automatic discovery that would let the framework find out about these objects on its own.
There are regular posts on the forum from users who try things like putting [Inject]
metadata onto a property and
then creating an instance of the class that contains the property with new
and expecting the injection to somehow magically
happen. For this to work there would be the need for a custom compiler or some runtime bytecode generation that would allow the
framework to add hooks to the object creation. Parsley provides neither of those options. It is meant to be used with the normal
mxmlc
and compc
compilers provided by the Flex SDK. Besides it would not make sense to provide these hooks anyway,
as Parsley is a modular, multi-Context environment, where it would not be obvious which Context such an enhanced object should be
wired to.
Of course that is equally true when an object gets created with new
inside another managed object. There is no
way for the framework to detect these calls. So if a managed object needs some collaborators they would usually need to be injected.
In rare cases it might create those instances itself and then add them to a Context explicitly as a Dynamic Object.
Therefore this section aims to provide an overview over the options to turn an object into a managed one and what services are available for such an object.
#####Types of Managed Objects
The types of managed objects can generally be divided into two categories.
Objects declared at Context initialization time in some configuration artifact such as:
- MXML Configuration
- XML Configuration Files
- Runtime Configuration
- Configuration DSL
- ActionScript Configuration
Objects dynamically added to an existing Context after it has been initialized using one of the following options:
- Explicit Component Wiring (a View Wiring Option)
- Automatic Component Wiring (another View Wiring Option)
- Dynamic Objects (for Programmatic Use)
- Publishing Managed Objects (based on the Decoupled Bindings Facility)
#####Services for Managed Objects
The level of services provided for the two categories listed in the previous section are nearly identical. The only difference is that the dynamically added objects in the second list are not available for injection into other objects since injection configuration is designed for robustness and validated at Context creation time. There are still other means to get hold of a dynamically added object inside other maneged objects such as Decoupled Bindings or Object Lifecycle Methods.
Apart from this difference all types of managed objects benefit from the full list of container services
Parsley provides. Basically this means all the features described in this manual, like Dependency Injection or
Messaging. The set of services for a particular
object is determined through processing its metadata tags and other configuration artifacts like nested tags inside
the tag in MXML configuration. The services are only provided for the time an object is managed.
So if it has a
[MessageHandler]
metadata tag for example
this method is invoked for all matching messages until the object gets removed from the Context or the Context
itself gets destroyed.
The lifetime of the object
varies depending on its type. An object declared with the tag in the root Context of the application
for example is often managed for the entire lifetime of the application. A Command represents the other
extreme: it is only managed as long as the command executes. An object configured in a Context for a Flex Module in turn
usually only lives until the Module gets unloaded. You can get notified when an object becomes managed or unmanaged inside
the object itself through its Object Lifecycle Methods.
Often the framework is also responsible for creating the object. In these cases it is able to provide
services like Constructor Injection. Sometimes though the object is created by the application and then
added to the Context, which obviously prevents that a service like Constructor Injection can be performed.
The most common ways to let the container instantiate the object is using configuration tags like ,
or
in an MXML or XML configuration file.
For the design of your application it is also important to consider that no object can get garbage collected as long as it is managed. For that reason a big and modular application should be carefully designed, making sure that you benefit from the support for modular applications that Parsley provides, allowing you to create and destroy entire child Contexts dynamically.
##Using Factories
Sometimes configuring the target object directly may not be sufficient. Maybe you want to execute some complex logic on object creation that would be difficult to setup declaratively. In these cases you can use a factory instead. The factory is a normal AS3 class that you can configure like any other class in Parsley, using Metadata, MXML or XML tags. The only difference is that one method is marked as a factory method (again using Metadata, MXML or XML):
class CategoryFactory {
public var categoryName:String;
[Inject]
public var bookManager:BookManager;
[Factory]
public function createCategory () : Category {
var books:Array = bookManager.getBooksForCategory(categoryName);
return new Category(categoryName, books);
}
}
You can then use this factory in your configuration, for example in MXML:
<Objects
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns="http://www.spicefactory.org/parsley"
xmlns:model="com.bookstore.model.*">
<fx:Declarations>
<model:BookManager/>
<model:CategoryFactory id="historyCategory" categoryName="history"/>
<model:CategoryFactory id="politicsCategory" categoryName="politics"/>
<model:CategoryFactory id="artsCategory" categoryName="arts"/>
</fx:Declarations>
</Objects>
Each of the factories we defined above will get the BookManager
instance injected and then produce
instances of Category
.
The special thing about using factories is that under the hood Parsley actually
creates two object definitions for each factory: One for the factory itself and one for the type the
factory method produces. This means that you can also place metadata tags on the target type (in this case the
Category
class) if you want the object that the factory produces to send and receive application messages
managed by Parsley for example.
This also means that you can use the factory and the objects it creates at injection points, although in most cases you'll be interested in the objects produced by the factory:
[Inject(id="historyCategory")]
public var historyBooks:Category;
It is recommended not to use factory methods with a return type of Object
like this:
[Factory]
public function createInstance () : Object {
It would work, Parsley would happily process this kind of factory method. But you'll lose some of Parsley's useful capabilities, since it uses the return type of the method for producing the object definition for the target type. If the target type is just Object, the metadata tags on the objects this factory actually produces would not be processed, since this happens before the factory method will be invoked for the first time. Furthermore you cannot use objects produced by such a factory for Dependency Injection by Type, since the type can only be determined dynamically. You would then be constrained to Injection by Id.
Of course, like with most other Parsley features, you may also declare the factory method in
MXML or XML. This may come in handy in some edge cases, for example for a factory that has more than
one method that produces objects. In this case placing metadata tags in the class itself would not
be possible (only one [Factory]
tag is allowed).
#####MXML Example
<Object id="monkey" type="{ZooFactory}">
<Factory method="createMonkey"/>
</Object>
<Object id="polarBear" type="{ZooFactory}">
<Factory method="createPolarBear"/>
</Object>
#####XML Example
<object id="monkey" type="com.example.ZooFactory">
<factory method="createMonkey"/>
</object>
<object id="polarBear" type="com.example.ZooFactory">
<factory method="createPolarBear"/>
</object>
##Asynchronous Object Initialization
A lot of operations in the Flash Player execute asynchronously. So it might happen that you want an object
configured in the Parsley IOC Container to load some data or assets first, before the rest of the Context gets
initialized and before this asynchronously initializing object gets injected into other objects. In this cases
you can use the [AsyncInit]
tag on any EventDispatcher
that fires events when the initialization
is completed (or if it has failed):
#hlt [AsyncInit] #hlt
public class FileLoader extends EventDispatcher {
public var filename:String;
#hlt [Init] #hlt
public function init () : void {
var loader:URLLoader = new URLLoader();
loader.addEventListener(Event.COMPLETE, fileLoaded);
loader.addEventListener(IOErrorEvent.IO_ERROR, handleError);
loader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, handleError);
loader.load(new URLRequest(filename));
}
private function fileLoaded (event:Event) : void {
// handle loaded file
#hlt dispatchEvent(new Event(Event.COMPLETE)); #hlt
}
private function handleError (event:ErrorEvent) : void {
#hlt dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, event.text)); #hlt
}
}
In the example above the [AsyncInit]
tag tells the framework that this is an asynchronously initializing
object. In the method annotated with [Init]
which will be invoked after configuration and
dependency injection has been processed for that object (see Object Lifecycle Methods for details) we start the
loading process. Depending on whether the loading succeeds or not we then dispatch either an Event.COMPLETE
or an ErrorEvent.ERROR
. The container will listen for those two events. In case of Event.COMPLETE
it
will proceed with Context initialization. In case of ErrorEvent.ERROR
the whole Context initialization process
will be cancelled.
#####Switching event types
Event.COMPLETE
and ErrorEvent.ERROR
are the default event types to signal whether initialization
has completed or failed. They can be switched with attributes of the [AsyncInit]
tag:
[AsyncInit(completeEvent="myCustomCompleteEvent",errorEvent="myCustomErrorEvent")]
#####Initialization Order
AsyncInit objects will always be initialized before regular objects unless you define an order attribute for them
explicitly which always has a higher precedence. But if you have more than one object marked with [AsyncInit]
you may want to declare the initialization order explicitly as explained in the next section. The order will not be determined
by analyzing the dependencies, as they are processed on the fly during initialization and also allow for bidirectional or
circular dependencies which would make it hard to determine the order automatically. But this really is only necessary for
AsyncInit objects and only if you wish to guarantee that those are ready when they are injected into other objects,
otherwise everything will be resolved automatically.
##Object Initialization Order
In case you want to explicitly specify the initialization order you can do that with the order attribute:
#####MXML Example
<Object id="welcomeText" type="{FileLoader}" order="1">
<AsyncInit/>
<Init method="init"/>
<Property name="filename" value="welcome.txt"/>
</Object>
#####XML Example
<object id="welcomeText" type="com.example.FileLoader" order="1">
<async-init/>
<init method="init"/>
<property name="filename" value="welcome.txt"/>
</object>
The default value if you omit this attribute is int.MAX_VALUE
so that all objects without an order attribute
will execute last and in arbitrary order. The attribute can be set to any positive or negative integer value.
The order attribute can also be used for objects that initialize synchronously. For any asynchronously initializing object in the sequence the container will wait for that object to complete its initialization before starting with the next one.
##Object Lifecycle Methods
If you want the Parsley Container to invoke methods on your object when it is created or when it is destroyed,
you can add the [Init]
or [Destroy]
metadata tags to the corresponding methods:
[Init]
public function init () : void {
[...]
}
[Destroy]
public function dispose () : void {
[...]
}
The methods marked with [Init]
get invoked after the object has been instantiated and
all injections have been processed.
The methods marked with [Destroy]
get invoked after the Context instance they belong to has been
destroyed with Context.destroy()
or when the object was removed from the Context.
For Flex Components declared in regular MXML files and wired to the Context as described in Dynamic View Wiring
the lifecycle is different: For those objects the methods get invoked whenever the object is added to or removed from the
stage respectively. Of course the [Destroy]
method additionally gets invoked if the Context the object
was wired to was destroyed.
##Lifecycle Observer Methods
Added in 2.2 this functionality opens some new doors for convenient ways to observe or modify other objects without the need to add something to their configuration. This might be particularly useful for short-lived objects like views or commands which might enter and leave the Context at any point in time and thus are not valid sources for regular injection points. By observing these instances you can still get hold of a reference to such a short-lived object.
[Observe]
public function enhance (panel:ShoppingCartPanel) : void;
Now this method will be invoked whenever a new instance of ShoppingCartPanel
(or any subclass) has
been fully configured.
The default without attributes like shown above is equivalent to:
[Observe(phase="postInit", scope="global")]
So you could also use a different phase of the lifecycle (like preDestroy
to get notified when an object leaves the Context) and can also control the scope and listen
only for matching types in the same Context with scope="local"
for example.
Scoping works the same way like scopes for messaging as explained in Using Scopes.
With this mechanism you simply plug in some new class that contains such a tag and suddenly existing classes
can be enhanced without the need to touch their configuration. This is somewhat analogous to the existing
[Factory]
tag which can be used to customize the object instantiation. With this tag you can customize
the object configuration after it has been instantiated. In both cases you do not even depend on the Parsley API
in any way.
#####Supported lifecycle phases
<tr>
<td><code>preInit</code></td>
<td>Invokes the observer after dependency injection has been performed but before the init method of
the object (if available) gets invoked.
<tr>
<td><code>postInit</code></td>
<td>The default if the phase attribute is omitted. Invokes the observer after dependency injection
has been performed and the init method of the object (if available) has been invoked.
<tr>
<td><code>preDestroy</code></td>
<td>Invoked when the object is removed from the Context but before the destroy method
on the object (if available) gets invoked.
<tr>
<td><code>postDestroy</code></td>
<td>Invoked when the object is removed from the Context and after the destroy method
on the object (if available) was invoked.
preConfigure |
Invokes the observer right after the object was instantiated but before any dependency injection was performed. |
##Dynamic Objects
(Note: Prior to version 2.3 the framework offered the DynamicContext
interface. This is still
available for backwards-compatibility, but deprecated and no longer required, since the core Context
interface now includes this functionality alongside a few new additional options).
In contrast to the usual root singleton objects specified with the tag, dynamic objects can be
added and removed from the Context dynamically. Dynamic Objects almost behave the same like regular objects.
In particular this means:
- You can inject any regular object into the dynamic object.
- The dynamic object can send and receive messages to and from any of the available scopes.
- The dynamic object can have lifecycle methods like
[Init]
and[Destroy]
.
There is only one significant restriction for using dynamic objects:
- You cannot inject a dynamic object into another object, unless it was specified with the
tag in MXML or XML.
This restriction is natural, since dependency injection comes with validation which would not be possible
if the set of objects available for injection could change at any point in time. For a more dynamic way
of expressing dependencies which more resembles the way Flex Bindings work you can use the [Subscribe]
tag introduced in version 2.3.
There are multiple ways to create a dynamic object:
#####Adding an existing object to the Context
var instance:Object = ...;
var dynamicObject:DynamicObject = context.addDynamicObject(instance);
In this case an ObjectDefinition
will be created for the existing instance and metadata will
be processed for the type of that instance, performing any required dependency injection, message receiver
registration or lifecycle method invocation.
The object can be removed from the Context at any point in time:
dynamicObject.remove();
At this point the method marked with [Destroy]
will be invoked on that object (if existent)
and any message receiver registrations will be terminated. The object is then fully removed from the Context.
#####Creating a dynamic object based on objects configured in MXML or XML
Dynamic objects can alternatively be configured in MXML or XML and then created dynamically at runtime based on these configurations:
<DynamicObject type="{SomeType}" id="dynamic1"/>
The tag above offers the same set of nested configuration tags like the tag.
You can then pull the dynamic objects from the Context by id:
var dynamicObject:DynamicObject = context.createDynamicObject("dynamic1");
Or alternatively fetched by type:
var dynamicObject:DynamicObject = context.createDynamicObjectByType(SomeType);
The instance created by the Context can then be accessed like this:
var instance:Object = dynamicObject.instance;
With the addition of the tag the use of the singleton attribute in the
Object
tag is deprecated: will continue to work, but will now get interpreted
like a
tag anyway, so is no longer required. The
tag should solely be used for
declaring singletons now.
#####The lifecycle of injected dynamic objects
Dynamic objects declared with the tag are the only types of dynamic objects which can be
injected into other objects the same way like singletons, since their defintion is available at Context creation time
and thus validatable. The way the object's lifecycle is controlled in this case is different than in those cases
where they are fetched from the Context by application code with
createDynamicObject
. In the latter case
the application will control the lifecycle and may remove the object from the Context at any point in time.
In case of an injection however it is the framework that controls the lifecycle. The injected object will
remain managed as long as the object it was injected into. It allows resource-friendly injections into dynamic objects
like commands.
#####Creating a dynamic object based on programmatically created object definitions
For building extensions which deal with object definitions from within a ObjectDefinitionDecorator
or RootConfigurationElement
implementation there are two interesting variants of the addDynamicObject
method
shown above. First it is possible to pass an additional ObjectDefinition
to the method:
var definition:ObjectDefinition = ...;
var instance:Object = ...;
var dynamicObject:DynamicObject = context.addDynamicObject(instance, definition);
In this case the definition will not be created by the dynamic Context like in the preceding example. Instead
the specified definition will be used. Since version 2.2 this mechanism will be used internally to support the new
option to configure dynamically wired views in MXML. An existing instance will have to be configured by an
ObjectDefinition
then which has been created elsewhere.
Finally you can also just pass a definition to the dynamic Context and it will create a new instance based on that definition:
var definition:ObjectDefinition = ...;
var dynamicObject:DynamicObject = context.addDynamicDefinition(definition);
Again, the instance can be accessed like this:
var instance:Object = dynamicObject.instance;
#####Removing a dynamic object from the Context
In all these different use cases removal of the object happens in the way already demonstrated:
dynamicObject.remove();
- Features List
- What's New in Parsley 3.0
- Migrating from Parsley 2 to Parsley 3
- Building the Framework from Source
- Dependencies
- Hello World Sample Application
- Adding the Framework SWCs
- Defining Object Dependencies
- Sending and Receiving Messages
- Assembling the Objects
- Initializing the Framework
- Adding more Services
Configuration and Initialization
- Configuration with AS3 Metadata
- MXML Configuration
- XML Configuration Files
- Runtime Configuration
- Configuration DSL
- ActionScript Configuration
- Combining multiple Configuration mechanisms
- Configuration Properties
- Constructor Injection
- Method Injection
- Property Injection by Type
- Property Injection by Id
- Declaring Dependencies in MXML or XML
- Overriding Dependencies in Child Contexts
- Comparing Dependency Injection and Decoupled Bindings
- Basic Usage
- Avoiding Conflicts
- Using Scopes
- Publishing Managed Objects
- Persistent Properties
- Bindings in Flash
- Dispatching Messages
- Receiving Messages
- Managed Events
- Injected MessageDispatchers
- MessageHandlers
- MessageBindings
- Intercepting Messages
- Error Handlers
- Using Selectors
- Using Scopes
- Using the Messaging API
- Implementing a Command
- Mapping Commands to Messages
- Command Groups
- Command Flows
- Starting a Command Programmatically
- Handling Results and Observing Commands
- Command Lifecycle
- About Managed Objects
- Using Factories
- Asynchronous Object Initialization
- Object Initialization Order
- Object Lifecycle Methods
- Lifecycle Observer Methods
- Dynamic Objects
- Initializing View Wiring Support
- Explicit Component Wiring
- Component Wiring without Reflection
- Automatic Component Wiring
- Metadata Configuration
- MXML and XML Configuration
- Component Lifecycle
- Flex Popups and Native AIR Windows
- View Wiring in Modular Applications
- Available Extension Points
- Custom Metadata Tags
- Custom MXML and XML Tags
- Working with ObjectDefinitions
- Working with Scopes
- Custom Configuration Mechanisms
- Replacing IOC Kernel Services
- Initializing Extension Modules