Flash Doctors Syringe User Guide

Revision History
Revision 1.0 DRAFT7/16/2006jt

Initial version of this document.

Revision 1.1 DRAFT7/18/2006jt

Finished chapters 3 & 4.

Revision 1.2 DRAFT11/15/2006jt

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

1. Chapter 1 - What is Syringe?
What it's good for
What it's NOT good for
Why would I use this instead of Flex?
Description
Components of Syringe
2. Chapter 2 - The Syringe XML File
Simple Bean Definitions
Constructor Injection
Property Injection
More on Value Definitions
Literal Strings
Literal Numbers
Literal Dates/Times
Literal Boolean Values
References to other objects
Maps (Dictionaries/Hashmaps)
Lists (Arrays/Collections)
More on Beans
Singleton vs. Non-Singleton Beans
Abstract Beans (Bean Templates)
Wrapping it all up
3. Chapter 3 - Using Syringe in a Simple Example
Creating an instance of XMLConfiguredContainer
Getting Objects out of the Container
A few other container features
4. Chapter 4 - Advanced Features
Advanced Bean Features
depends-on
init-method
post processors
Lazy vs. Non-Lazy behavior.
Using public properties & Syringe
5. Baking your config file into a custom container class
Baking your container
Using the raw XSLT
Using ANT to bake your config

List of Examples

2.1. A simple bean.
2.2. Passing a few constructor arguments to a bean
2.3. Alternate shorthand version for setting constructor arg values
2.4. Passing constructor arguments using specified indexes
2.5. Setting property values
2.6. Alternate shorthand for setting property values
2.7. Setting "add" style properties
2.8. a string
2.9. a few examples of numbers
2.10. a few examples of dates/times
2.11. A few examples of boolean values
2.12. Pointing to a bean with the id of "bean2"
2.13. A map with strings as keys and values
2.14. A map with beans as values
2.15. A map with objects as values and keys
2.16. Shorthand for maps
2.17. A list of mixed values
2.18. Creating a singleton bean
2.19. Abstract bean example using properties
2.20. Abstract bean example using constructor-arg
2.21. Abstract bean example using constructor-arg index
2.22. Basic config file
2.23. A simple full example
2.24. A longer example, using most of the possible concepts
3.1. Referencing classes from the XML so they get included in your final SWF
3.2. Waiting for XMLConfiguredContainer to load
3.3. Getting a bean out of the container
3.4. Getting a strongly typed bean out of the container
3.5. Not often used container features
4.1. An example where you might want to use depends-on
4.2. An example where you would not need to use depends-on (but still can if you wish)
4.3. Using the IInitialzingBean interface
4.4. Specifying what function to run after bean has been created
4.5. How one might use bean post processors
4.6.
4.7.

Chapter 1. Chapter 1 - What is Syringe?

What it's good for

  • 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

What it's NOT good for

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

Why would I use this instead of Flex?

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

Description

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.

Components of Syringe

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.

Chapter 2. Chapter 2 - The Syringe XML File

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.

Simple Bean Definitions

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.

Example 2.1. A simple bean.

<bean id="bean1" class="com.flashdoctors.test.SimpleObject"/>

Constructor Injection

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

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>

More on Value Definitions

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.

Literal Strings

String is the default value type.

Example 2.8. a string

<value>Here is a string.</value>

Literal Numbers

Example 2.9. a few examples of numbers

<value>123</value>
<value>-123.456</value>

Literal Dates/Times

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>  

Literal Boolean Values

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.

Example 2.11. A few examples of boolean values

<value>1</value>
<value>false</value>

References to other objects

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.

Example 2.12. Pointing to a bean with the id of "bean2"

<ref bean="bean2"/>

Warning

Spring developers: the local attribute is not currently supported.

Maps (Dictionaries/Hashmaps)

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 (Arrays/Collections)

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>

More on Beans

Singleton vs. Non-Singleton Beans

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.

Warning

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.


Abstract Beans (Bean Templates)

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>

Wrapping it all up

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>

Chapter 3. Chapter 3 - Using Syringe in a Simple Example

Creating an instance of XMLConfiguredContainer

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

Getting Objects out of the Container

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

Example 3.3. Getting a bean out of the container

var b1:Object = container.getBean("bean1");

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.

A few other container features

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

Chapter 4. Chapter 4 - Advanced Features

Advanced Bean Features

depends-on

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>

init-method

If you'd like to perform something after all the properties have been set on your bean, you may use one of two methods:

Implementing the com.flashdoctors.syringe.bean.IInitializingBean interface

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

Using the init-method XML attribute

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

post processors

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.

Note

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

Lazy vs. Non-Lazy behavior.

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>

Using public properties & Syringe

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>

Chapter 5. Baking your config file into a custom container class

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

Warning

This is an experimental feature. It's not quite complete yet - many flavors of maps are not supported for instance.

Baking your container

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.

Using the raw XSLT

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.

Using ANT to bake your config

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>