Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3334610
  • 博文数量: 530
  • 博客积分: 13360
  • 博客等级: 上将
  • 技术积分: 5473
  • 用 户 组: 普通用户
  • 注册时间: 2006-07-13 13:32
文章分类

全部博文(530)

文章存档

2017年(1)

2015年(2)

2013年(24)

2012年(20)

2011年(97)

2010年(240)

2009年(117)

2008年(12)

2007年(8)

2006年(9)

分类:

2010-05-06 21:16:08

    本章对配置文件进行额外说明。

6.1使用工厂(Using Factories)

    有时候,直接配置目标对象不可能有效。可能你在对象创建时想执行一些复杂的逻辑,以至于你想用工厂模式代替现在模式。AS3中有一个factory类,你可以象其它类一样配置它,使用Metadata, MXML 或 XML标签。不同之处在于方法被标识成了factory方法:

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);
}
}

    你也能使用这个factory在你的配置文件中。

            xmlns:mx=""
xmlns:model="com.bookstore.model.*">







    我们上面定义的每一个factories将获得BookManager实例注入,并且产生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







XML Example







6.2 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):

[AsyncInit]
public class FileLoader extends EventDispatcher {

public var filename:String;

[Init]
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
dispatchEvent(new Event(Event.COMPLETE));
}

private function handleError (event:ErrorEvent) : void {
dispatchEvent(new ErrorEvent(ErrorEvent.ERROR, false, false, event.text));
}

}

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 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.

6.3 Object Initialization Order

In case you want to explicitly specify the initialization order you can do that with the order attribute:

MXML Example






XML Example






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.

6.4 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. See for details.

For Flex Components declared in regular MXML files and wired to the Context as described in 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.

6.5 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 .

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

preConfigure Invokes the observer right after the object was instantiated but before any dependency injection was performed.
preInit Invokes the observer after dependency injection has been performed but before the init method of the object (if available) gets invoked.
postInit 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.
preDestroy Invoked when the object is removed from the Context but before the destroy method on the object (if available) gets invoked.
postDestroy Invoked when the object is removed from the Context and after the destroy method on the object (if available) was invoked.

6.6 Dynamic Objects

Since version 2.1 there is a new interface DynamicContext. Such a Context allows you to dynamically add and remove objects from the Context. In most cases this is not intended to be used by normal application code, but you can easily build extensions on top of that functionality that require some more flexibility than the regular Context. Internally the DynamicContext will be used for wiring views which are added and removed from the Context depending on the time they are placed on the stage and (starting from version 2.2) for short-lived Command objects which are only added to the Context to perform their asynchronous operation and then immediately get removed again.

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.

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. This is no real limitation anyway since you can get hold of these dynamic objects through their lifecycle methods, in case you want to get notified whenever an object of a particular type enters the Context (or any scope of Contexts).

You can create a dynamic Context from a regular one:

var dynamicContext:DynamicContext = context.createDynamicContext();

This way the dynamic Context is connected to the regular one and all objects living in that Context or one of its parents can be injected into the dynamic objects. You can then simply add any object to that Context:

var instance:Object = ...;
var dynamicObject:DynamicObject = dynamicContext.addObject(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.

For building extensions which talk to a DynamicContext instance from within a ObjectDefinitionDecorator or ObjectDefinitionFactory implementation there are two interesting variants of the addObject method shown above. First it is possible to pass an additional ObjectDefinition to the method:

var definition:ObjectDefinition = ...;
var instance:Object = ...;
var dynamicObject:DynamicObject = dynamicContext.addObject(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. In 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 = dynamicContext.addDefinition(definition);

The instance created by the Context can then be accessed like this:

var instance:Object = dynamicObject.instance;

In all these different use cases removal of the object happens in the way already demonstrated:

dynamicObject.remove();
阅读(1544) | 评论(0) | 转发(0) |
0

上一篇:parsey常用错误

下一篇:parsley例2-配置文件

给主人留下些什么吧!~~