Revision History | ||
---|---|---|
Revision 1.0 DRAFT | 7/16/2006 | jt |
Initial version of this document. | ||
Revision 1.1 DRAFT | 7/18/2006 | jt |
Finished chapters 3 & 4. | ||
Revision 1.2 DRAFT | 11/15/2006 | jt |
Cleanup, adding new/changed features. |
Flash Doctors Syringe User Guide
Syringe is the dependency injection container implementation from FlashDoctors. This book outlines how an ActionScript developer should use the Syringe module.
Table of Contents
List of Examples
Table of Contents
tends to lead to more re-usable code (decouples objects from each other - dependancies are fufilled by the container)
seperation of development from appliction maintainenace & assembly. Advanced developers can write plain classes that do useful things, and less experienced developers can use them without writing actionscript.
consitant, powerful config file format that can be common across projects and development teams
Storing large lists of data. Large lists of data are better stored in a format designed for that type of data. Syringe is a general purpose "assembler" language and should be used to "put together the lego blocks" of you application. You could write an object that takes an XML file of the more specialized format, and configure it with syringe (ie: pass in the data XML path, etc). All that being said, if your application doesn't have a lot of data, it's really handy to put it into the syringe file...
Making your movie smaller. The XML format for syringe is not meant to be ultra efficient - it's actually quite large when compared with other solutions. It's meant to be very generalized, and as such, ends up being on the large side. See the chapter on "baking" your config file, as this greatly reduces this effect, at the cost of not being able to update the container at runtime.
Flex is required to compile the config file into a movie. Syringe does it at runtime, so no special tools are required to update the movie other than an XML/text editor. Again, see the chapter on "baking" your config if you're interested in an approach that doesn't use an external XML file.
Flex compiles to Flash 9, some sites still maintain Flash 7 or 8 as their standard.
Syringe is more comfortable for Java/Spring programmers - they don't have to learn a new way of doing things
We didn't make this up - we borrowed from Spring - already proven & the Java industry generally agrees that it's a good way of doing things...
Syringe helps you along the path of writing & working with more reusable code. It solves an issue that comes up when you properly de-couple your objects from each other.
This loose coupling can be acomplished by getting rid of any object creation that happens inside your objects , and instead obtaining references to those collaborating objects from an outside source. Syringe is this outside source - it sets up the objects of your application and links them together for you. This frees one from having to write all the "wiring" code for an application.
Rather directly instantiating or linking up with other objects, you write your objects to accept their collaborating objects via "setter" or "adder" methods which accept a paramter typed by an interface. Using an interface allows you to be gaurenteed that you're going to get an object that has the methods you need, but gives you the flexibility to pass in completly different implementations of those methods. (incidently, Syringe doesn't force you to use interfaces, you can use normal classes if you prefer)
This method of wiring up an application is called dependancy injection. For a deeper, much more accurate explanation of dependancy injection please read Martin Fowler's exellent article on the subject.
Syringe is made up of a number of components, most of which you won't need to worry about. The important one to remember for now is the IBeanContainer interface, and the XMLConfiguredContainer class. IBeanContainer is the interface that all Syringe container types will adhere to. XMLConfiguredContainer adheres to IBeanContainer, and will allow you to define your beans in XML. Another component to be familiar with is the BeanDefinition class. While you don't normally create instances of BeanDefinition by hand, you do specify them by way of an XML configuration file. Bean definitions are descriptions of how to create an instance of an object. You may then "inject" values into the constructor of that object, or set properties on that object.
Table of Contents
This chapter descirbes how to lay out your configuration file for the XMLConfiguredContainer.
There are two main ways to have Syringe wire up your objects: constructor injection and property injection. These two methods may be used interchangably, or in combination. No matter which type of injection you use, the way you specify values for them is the same.
You may define your objects using the
bean
tag, and the
id
and class
attributes.
id
allows you to give your bean a name.
This will allow you to reference this bean in the config file, as
well as obtain this bean by name in your code.
class
allows you to specify what
ActionScript class this bean is. Specify the full ActionScript
class path, not just the class name.
Constructor injection refers to having the container inject
values into an object's constructor. You may give a bean
constructor arguments by using the
constructor-arg
tag, which has the
type
attribute, and index
attribute.
type
is optional, and
allows you to give an indication of what type the literal value
is you're specifying. Valid values are String, Number, Date,
and Boolean.
index
is also optional, and
allows you to specify which argument number you' working with.
This index is 1 based - so an index of 0 is not valid. This is mostly
used when working with abstract beans.
Example 2.2. Passing a few constructor arguments to a bean
<bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Hello World</value></constructor-arg> <constructor-arg type="Number"><value>123.45</value></constructor-arg> <constructor-arg><ref bean="bean2"/></constructor-arg> </bean>
Example 2.3. Alternate shorthand version for setting constructor arg values
<bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <constructor-arg value="Hello World"/> <constructor-arg type="Number" value="123.45"/> <constructor-arg ref="bean2"/> </bean>
Example 2.4. Passing constructor arguments using specified indexes
<bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <constructor-arg index="3" value="Hello World"/> <constructor-arg index="1" type="Number" value="123.45"/> <constructor-arg index="2" ref="bean2"/> </bean>
Property injection refers to having the container set
property values on objects. You may give a bean property values
by using the property
tag along with the
name
, type
and mode
attributes.
name
allows you to
specify which property you're working with. When actually
going to set the value, Syringe will look for a method named
setXyz where xyz is the property name. In other words, if you
have a property called "id", it would look for setId
when setting the value of this property.
type
is optional, and allows you to give
an indication of what type the literal value is you're
specifying. Valid values are String, Number, Date, and
Boolean.
mode
is optional, and allows you set
multiple values on "add" style properties.
Acceptable values are "add" and "set".
Example 2.5. Setting property values
<bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <property name="title"><value>Hello World</value></property> <property name="id" type="Number"><value>12345</value></property> <property name="object"><ref bean="bean2"/></property> </bean>
Example 2.6. Alternate shorthand for setting property values
<bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <property name="title" value="Hello World"/> <property name="id" type="Number" value="12345"/> <property name="object" ref="bean2"/> </bean>
Example 2.7. Setting "add" style properties
This will be the equivilent of calling "addTitle" 3 times on the bean1 object (once for each item in the list)
<bean id="bean1" class="com.flashdoctors.test.TitleList"> <property name="title" mode="add"> <list> <value>Title 1</value> <value>Title 2</value> <value>Title 3</value> </list> </property> </bean>
There are a number of different types of values you may define: "static" value, or literal values. References to other beans, maps, and lists. Be sure to read all the examples, as they show shorthand versions as well.
String is the default value type.
Represent dates/times in standard W3C date format, with one exception - don't use the decimal fractions of a second. Your options are YYYY-MM-DD (date only), hh:mm:ssTZD (time only, date will be today), or YYYY-MM-DDThh:mm:ssTZD (where the "T" character is the seperator between date and time.
Example 2.10. a few examples of dates/times
<value>2006-07-18</value> <value>14:23:17-06:00</value> <value>2006-07-18T14:23:17-06:00</value>
You may represent a boolean of true as either a "1", or as "true", and the boolean value of false as "0" or "false". Any other value will not be treated as a boolean.
You may point to another bean by using the
ref
tag. You may specify the bean you'd
like to point at using the bean
attribute.
Spring developers: the
local
attribute is not currently
supported.
Maps: If you'd like to pass a dictionary-like
structure to a constructor argument or property, use the
map
, entry
, and
key
tags. Objects created by the map tag adhere to
the com.flashdoctors.util.IMap interface. Your object's setter/constructor arg should accept a paramter of that interface.
Example 2.13. A map with strings as keys and values
<map> <entry> <key>something1</key> <value>value for something 1</value> </entry> <entry> <key>something2</key> <value type="Number">123</value> </entry> </map>
Example 2.14. A map with beans as values
<map> <entry> <key>pointer_a</key> <ref bean="bean1"/> </entry> <entry> <key type="Number">1</key> <ref bean="bean2"/> </entry> </map>
Example 2.15. A map with objects as values and keys
<map> <entry> <key><ref bean="beanA"/></key> <ref bean="bean1"/> </entry> <entry> <key><ref bean="beanB"/></key> <ref bean="bean2"/> </entry> </map>
Example 2.16. Shorthand for maps
<map> <entry key="k" value="abc, 123"/> <entry key="t" value="124" value-type="Number"/> <!-- key-ref points at another bean as the key value --> <entry key-ref="bean1" value="2352345"/> <!-- value-ref points at another bean as the value --> <entry key="j" value-ref="bean2"/> <!-- objects as key and value --> <entry key-ref="bean1" value-ref="bean2"/> </map>
Lists: If you'd like to pass an array-like structure
to , use the list
tag. Objects created by the list tag adhere to
the com.flashdoctors.util.IList interface. Your object's setter/constructor arg should accept a paramter of that interface.
Example 2.17. A list of mixed values
<list> <value>123</value> <ref bean="bean1"/> <value>My description</value> <value>2006-07-09</value> </list>
By default, all beans in Syringe are singletons within that container
, that is
to say: for a given bean tag, there is 1 and only 1 instance
created for that bean definition. You may, however, want a new
instance created each time you call for a bean. In this case,
you may specify singleton
=
false
. Each time you
reference that bean, or want to get it out of the container, a
brand new instance will be created.
This concept of singleton may be different from other singleton pattern implementations you have seen. In many cases, when you say "singleton", you would assume that would mean 1 instance of a given object type per runtime environment. Remember, the notion of singleton in Syringe is slightly different as it refers to the container, not the runtime.
Example 2.18. Creating a singleton bean
<bean id="randomNumber" class="com.flashdoctors.util.RandomNumber"/> <bean id="bean1" class="com.flashdoctors.test.RandomizedBean"> <property name="myRandomNumber"><ref bean="randomNumber"/></property> </bean> <bean id="bean2" class="com.flashdoctors.test.RandomizedBean"> <property name="myRandomNumber"><ref bean="randomNumber"/></property> </bean>
In this example, if the RandomNumber class generated a unqiue random number every time it was created (accessable by calling toNumber() on it ), and the setMyRandomNumber method on the RandomizedBean objects took those RandomNumber instances. Each one would have a different random number to work with.
Now, you may think to yourself, these bean definitions are kinda long... and indeed, you'd be correct. The goal of Syringe is not to make defining objects less verbose. But there is a solution to at least partially aleviate long XML files.
Abstract beans allow you to specify many aspects of a bean, then re-use those aspects to stamp out new beans. You can think of it as a parent child relationship - child beans inherit information from their parent.
Only certian aspects of a bean definition may be inherited. These include the class attribute, property definitions, and constructor-arg definitions. The id attribute, and singleton attribute don't get inherited.
Example 2.19. Abstract bean example using properties
In this example, 2 beans could be created, bean1, and bean2. Both would be of type SimpleObject, and both would have title values of "Default Title", but bean1 would have and id value of 1, and bean2 would have an id value of 2.
<bean id="templateA" abstract="true" class="com.flashdoctors.test.SimpleObject"> <property name="title"><value>Default Title</value></property> </bean> <bean id="bean1" parent="templateA"> <property name="id"><value>1</value></property> </bean> <bean id="bean2" parent="templateA"> <property name="id"><value>2</value></property> </bean>
Example 2.20. Abstract bean example using constructor-arg
In this example, bean1 and bean2 would both have "5" as their second constructor arg, but each would have a unique first constructor-arg.
<bean id="templateA" abstract="true" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Default Title</value></constructor-arg> <constructor-arg type="Number"><value>5</value></constructor-arg> </bean> <bean id="bean1" parent="templateA"> <constructor-arg><value>Bean 1 Title</value></constructor-arg> </bean> <bean id="bean2" parent="templateA"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> </bean>
Example 2.21. Abstract bean example using constructor-arg index
When inheriting with constructor args, it's done by order of the constructor args. This is where the index attribute comes in handy, as it allows you to override certain constructor args.
In this example, bean1 and bean2 would both have "Default Title" as their first constructor arg, but each would have a unique 2nd and 3rd constructor-arg.
<bean id="templateA" abstract="true" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Default Title</value></constructor-arg> <constructor-arg type="Number"><value>5</value></constructor-arg> <constructor-arg><value>Category</value></constructor-arg> </bean> <bean id="bean1" parent="templateA"> <constructor-arg index="2" type="Number"><value>7</value></constructor-arg> <constructor-arg index="3" ><value>Category A</value></constructor-arg> </bean> <bean id="bean2" parent="templateA"> <constructor-arg index="2" type="Number"><value>8</value></constructor-arg> <constructor-arg index="3" ><value>Category B</value></constructor-arg> </bean>
The config file should always be structured like so
Example 2.22. Basic config file
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> ... bean tags go here ... </beans>
Example 2.23. A simple full example
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="bean1" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 1 Title</value></constructor-arg> <constructor-arg type="Number"><value>1</value></constructor-arg> <property name="date" type="Date"><value>2005-01-01</value></property> </bean> <bean id="bean2" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> <constructor-arg type="Number"><value>2</value></constructor-arg> <property name="date" type="Date"><value>2007-01-01</value></property> </bean> <bean id="container1" class="com.flashdoctors.test.ExpiringContainer"> <property name="expirationDate" type="Date"> <value>2006-07-01</value> </property> <property name="items"> <list> <ref bean="bean1"/> <ref bean="bean2"/> </list> </property> </bean> </beans>
Example 2.24. A longer example, using most of the possible concepts
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="templateA" abstract="true" class="com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Default Title</value></constructor-arg> <constructor-arg type="Number"><value>5</value></constructor-arg> </bean> <bean id="bean1" parent="templateA"> <constructor-arg><value>Bean 1 Title</value></constructor-arg> </bean> <bean id="bean2" parent="templateA"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> </bean> <bean id="bean3" parent="templateA"> <constructor-arg><value>Bean 3 Title</value></constructor-arg> </bean> <bean id="bean4" parent="templateA"> <constructor-arg><value>Bean 4 Title</value></constructor-arg> </bean> <bean id="randomNumber" singleton="false" class="com.flashdoctors.util.RandomNumber"/> <bean id="container1" class="com.flashdoctors.test.ContentList"> <constructor-arg> <value>my_content</value> </constructor-arg> <property name="currentItemNumber"> <ref bean="randomNumber"/> </property> <property name="items"> <list> <ref bean="bean1"/> <ref bean="bean2"/> <ref bean="bean3"/> <ref bean="bean4"/> </list> </property> </bean> </beans>
Table of Contents
After you have a configuration file ready, you're almost set to start running an application. There are a few important details though:
1) Remember, currently, beans are not actually created until you get them out of the container for the first time. This is called "lazy-instaciation." So, if you create the container and load the XML file, nothing will happen until you start getting beans out and doing things with them.
2) Make sure when you compile your application that all classes used in the XML file get included. The general way to do this in ActionScript is to simply reference the class names somewhere in your code. For Syringe, we prefer something like this
Example 3.1. Referencing classes from the XML so they get included in your final SWF
// first, import the classes used in the XML file import com.flashdoctors.test.SimpleBean; import com.flashdoctors.test.ContentList; class MyApplication { // second, references the classes somewhere, like so private static var usedClasses:Array = [SimpleObject, ContentList] // alternativly, if you don't want to use the import statements, you may use this form private static var usedClasses:Array = [com.flashdoctors.test.SimpleObject, com.flashdoctors.test.ContentList] function MyApplication(){ } }
3) Make sure you properly wait for the container to do it's thing before you do yours. This is done by way of implementing ILoadListener.
Example 3.2. Waiting for XMLConfiguredContainer to load
// first, import the classes used in the XML file import com.flashdoctors.test.SimpleBean; import com.flashdoctors.test.ContentList; import com.flashdoctors.reflex.DelegateCommand; import com.flashdoctors.net.ILoadEventListener; import com.flashdoctors.net.LoadEvent; class MyApplication { // second, references the classes somewhere, like so private static var usedClasses:Array = [SimpleObject, ContentList] // a variable to hold the container private var container:XMLConfiguredContainer; function MyApplication() implements ILoadEventListener { container = new XMLConfiguredContainer(); container.addLoadEventListner( this ); // now actually tell the container where your config file is. // it will start loading immediatly. container.load("myconfig.xml"); } function onLoadEvent( evt : LoadEvent ){ if( evt.isComplete() && ! evt.isError() ){ doMyThing(); } } function doMyThing():Void{ // now you can get beans out and start working with them. } }
Now that you know how to propertly include the correct classes used in your config, create the container, point it to your config, and wait for it to load, you're ready to start using the beans you've defined. This is actually the simple part...
At that instant, the container would find the definition for the bean with an id of "bean1", and actually create the object and return it to you. Under normal circumstances, you probably want to make sure the object you get out is of a certain type, so you'd cast it like so:
Example 3.4. Getting a strongly typed bean out of the container
var b1:SimpleObject = SimpleObject(container.getBean("bean1"));
One very handy thing is that if you get a bean out that references other beans that haven't been created, the container will create them for you automatically.
There are a few other features of the container you may find useful, we won't go into much detail as they are not used very often.
Example 3.5. Not often used container features
// add an already created bean to the container - use with caution... var someObject:SimpleObject = new SimpleObject("hello"); container.setBean( "bean2", someObject ); // see if a bean exists - returns true or false if( container.beanExists("bean1") ){ trace("yip, bean1 is there..."); } else { trace("no bean1 for you."); } // get a list of all the beans available (returns a list of Strings) var beans:List = container.getBeanNames();
Table of Contents
You may want to make sure that when one bean is created, that some others get created as well. This happens for you automatically if the bean you're getting out references the other beans. If your beans don't directly reference each other, you can use depends-on. You may specify any number of beans, simply seperate their names with commas.
In the folowing example, if you get bean1 out, and bean2 and bean3 haven't been created, they will be. In fact, they will be created BEFORE bean1 is created.
Example 4.1. An example where you might want to use depends-on
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="bean1" "com.flashdoctors.test.SimpleObject" depends-on="bean2,bean3"> <constructor-arg><value>Bean 1 Title</value></constructor-arg> <constructor-arg type="Number"><value>1</value></constructor-arg> </bean> <bean id="bean2" "com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> <constructor-arg type="Number"><value>2</value></constructor-arg> </bean> <bean id="bean3" "com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> <constructor-arg type="Number"><value>2</value></constructor-arg> </bean> </beans>
In the following example, you don't really need to use depends-on because container1 directly references the other beans.
Example 4.2. An example where you would not need to use depends-on (but still can if you wish)
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="bean1" "com.flashdoctors.test.SimpleObject" depends-on="bean2,bean3"> <constructor-arg><value>Bean 1 Title</value></constructor-arg> <constructor-arg type="Number"><value>1</value></constructor-arg> </bean> <bean id="bean2" "com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> <constructor-arg type="Number"><value>2</value></constructor-arg> </bean> <bean id="bean3" "com.flashdoctors.test.SimpleObject"> <constructor-arg><value>Bean 2 Title</value></constructor-arg> <constructor-arg type="Number"><value>2</value></constructor-arg> </bean> <bean id="randomNumber" singleton="false" "com.flashdoctors.util.RandomNumber"/> <!-- the depends-on in this bean is actually not required, as the beans it includes are already directly referenced and would be created anyway --> <bean id="container1" "com.flashdoctors.test.ContentList" depends-on="randomNumber,bean1,bean2"> <constructor-arg> <value>my_content</value> </constructor-arg> <property name="currentItemNumber"> <ref bean="randomNumber"/> </property> <property name="items"> <list> <ref bean="bean1"/> <ref bean="bean2"/> </list> </property> </bean> </beans>
If you'd like to perform something after all the properties have been set on your bean, you may use one of two methods:
This method is somewhat more automatic. If a bean is created that has a function called "afterPropertiesSet", it wil be called after all the properties are set, and before the bean is retured from getBean. Note - this will only happen when the bean first get's created. Subsequent calls to getBean will not trigger more calls to afterPropertiesSet.
Example 4.3. Using the IInitialzingBean interface
import com.flashdoctors.syringe.bean.IInitializingBean; class MyAdditionBean implements IInitializingBean { private x:Number; private y:Number; private z:Number; public function MyAdditionBean(){ x = 0; y = 0; z = 0; } public function setX( n:Number ):Void{ this.x = n }; public function setY( n:Number ):Void{ this.y = n }; public funciton getZ():Number{ return this.z }; public function afterPropertiesSet():Void{ this.z = this.x + this.y; } }
If for some reason you don't want to, or can't implement the IInitializingBean interface, do not dispair, you may also specify the method name to be run in the XML
Example 4.4. Specifying what function to run after bean has been created
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="bean1" "MyAdditionBean" init-method="doAddition"> <property name="x" value="5" type="Number"/> <property name="y" value="3" type="Number"/> </bean> </beans>
class MyAdditionBean { private x:Number; private y:Number; private z:Number; public function MyAdditionBean(){ x = 0; y = 0; z = 0; } public function setX( n:Number ):Void{ this.x = n }; public function setY( n:Number ):Void{ this.y = n }; public funciton getZ():Number{ return this.z }; public function doAddition():Void{ this.z = this.x + this.y; } }
Bean post processing can be quite useful if you need to include extra logic to go modify properties or call methods after your beans have been created. You do this by creating a class that implements com.flashdoctors.syringe.bean.IBeanPostProcessor, which lays out 2 methods: postProcessBeforeInitialization, and postProcessAfterInitialzation. These methods will be called with a reference to the bean being processed. You must return the bean you'd like to be used in it's place. This can be as simple as returning the bean you were given, or taking that bean, wrapping it in some other proxy object, and returning the proxy. It's up to you, just make sure you return something (or null if you really want to... this will effectivly kill the bean though)
postProcessBeforeInitilization will be called after the bean is created, and after all properties are set, but before the init-method is called.
postProcessAfterInitilization will be called after init-method has had a chance to run.
Syringe currently will perform a scan for classes that implement these two methods before it's done loading. If it finds any beans that implement both methods, those beans will be isntanciated immediatly and NOT lazy-loaded like all other beans.
The following example shows a very simple way that a post processor could be used. This one saves you from having to specify the url prefix for all your filenames in the XML - you give the post processor the prefix, then just specify filenames for all the locations.
Example 4.5. How one might use bean post processors
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe"> <bean id="bean1" class="BasePathSetter"> <property name="basePath" value="http://www.example.com/images/project1/"/> </bean> <bean id="image1" class="Image"> <property name="location" value="test.jpg"/> </bean> <bean id="xml1" class="XMLFile"> <property name="location" value="test.xml"/> </bean> <bean id="movie1" class="Movie"> <property name="location" value="test.swf"/> </bean> </beans>
interface ILocatable { public function setLocation( loc:String ):Void; public function getLocation():String; }
class Image implements ILocatable, IInitilizingBean { // define Image class here... // including setLocation and getLocation methods public function afterPropertiesSet(){ // actually load the image here... } } class XMLFile implements ILocatable, IInitilizingBean { // define XMLFile class here... // including setLocation and getLocation methods public function afterPropertiesSet(){ // actually load the xml here... } } class Movie implements ILocatable, IInitilizingBean { // define Movie class here... // including setLocation and getLocation methods public function afterPropertiesSet(){ // actually load the SWF here... } }
import com.flashdoctors.syringe.bean.IBeanPostProcessor; class BasePathSetter implements IBeanPostProcessor { private basePath:String; public function BasePathSetter(){ } public function setBasePath( bp:String ){ this.basePath = bp; } public function postProcessBeforeInitialization( bean:Object, beanName:String ):Object{ var loc:ILocatable = ILocatable(bean); if( loc != null ){ // we have something that is locatable, go ahead and process it // in this case, we're going to add a URL prefix to the location loc.setLocation( this.basePath + loc.getLocation() ); } else { // this object is not locatable, so don't do anything, just return it return bean; } } public function postProcessAfterInitialization( bean:Object, beanName:String ):Object{ // we've already done our thing in postProcessBeforeInitialization, so just return the bean... return bean; } }
By default, Syringe will instanciate your objects only when asked to, or antoher object depensd on them. If you'd like all objects to be created, in the order specified in your config file (while adhering to dependancy rules) you may specify default-lazy-init="false" on the beans tag of your config file.
Example 4.6.
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe" default-lazy-init="false" > ... bean tags go here ... </beans>
If you'd like to use objects that don't have getters, setters, or constructor args exposed, but do have public properties (many build in flash classes are like this...) turn on blindly-set-properties="true". In the absense of a setter method for a given property, syringe will forcefully set the property, like so: obj["propertyname"] = value;. If that property doesn't exist, it will after syringe get's done with it... hehe.
Example 4.7.
<?xml version="1.0" charset="UTF-8"?> <beans xmlns="http://www.flashdoctors.com/2006/07/Syringe" blindly-set-properties="true" > ... bean tags go here ... </beans>
Table of Contents
Baking your config into an AS file allows you to create a static version of your application. It saves on final file size, and is faster as well, as no XML parsing will be done at runtime. This is not recommended for large (> 100k) XML config files, for now. This is actually very similiar to what FLEX does behind the scenes...
This is an experimental feature. It's not quite complete yet - many flavors of maps are not supported for instance.
In order to bake your config file, you need to check out the SyringeContainerBaker project from the SVN repository. This project contains the XSLT file used for baking, as well as an ANT build file that you may use.
You may bake your config using any standards compliant XSLT processor. The only tricky thing is you have to pass a few paramters into the XSLT.
class_name - the final class name you'd like your container to have.
package_name - the package you'd like your container to have.
It's easy to do an XSL Transform in ANT, simply use the template below, and create a task in your ANT file for baking. We reccommend outputting generated classes like this into a seperate source tree, so it's not tangled up with your hand-written code.
<xslt style="path/to/bake.xslt" in="path to your config" out="generated_src/com/package/MyClass.as"> <param name="class_name" expression="MyClass"/> <param name="package_name" expression="com.package"/> </xslt>