AgE 2.6 : IoC Container

On this page:

Motivation

AgE comes bundled with a custom IoC Container.

Wait, don't run away yet! We are fully aware of Spring, Guice or Picocontainer. AgE initial requirements weren't fully met by any of these libraries, which led to the development of a custom solution. In fact, it is internally based on Picocontainer and provides a superset of these different libraries functionality.

AgE's IoC container: 

  • is fast, lightweight and extensible (like Picocontainer)
  • can be configured in XML (like Spring), but also in custom DSLs (like Guice or Picocontainer)
  • can inject explicit dependencies and properties, but also implicitly autowire components (JSR-330)
  • allows hierarchical configurations (component definitions can be nested and overridden)
  • is reconfigurable on the fly

Actually, if you ever used Spring, Guice or Picocontainer, you shouldn't have much trouble starting working with AgE's IoC container.

Service Locator

An IoC container usually realizes the Service Locator pattern. As such, it behaves as a registry in which components can be registered. These components can be later on looked up by name or type. 

It should be noted that what is actually registered in the container are usually not instances themselves, but component definitions. Such a definition contains all the information needed to correctly instantiate, initialize and wire up a component. Each look up in the container will usually result in a new instance being constructed from the definition. Components can however be defined as singletons, in which case a single instance will be cached and returned each time. 

Hierarchical Containers

AgE makes use of picocontainer scoped containers to provide an interesting feature: The container itself is usually decomposed into a hierarchy of smaller containers. If it does not contain a given component, a child container will delegate the lookup to its parent.

It is thus possible to encapsulate some parts of the components graph, dynamically extend or prune it during runtime as well as allow for component implementation shadowing.

 

Shadowing the implementation of a component fixes the so-called robot legs problem, which emerges in the context of IoC containers. This problem occurs when instantiating multiple components which are configured almost identically (robot legs), but differ in a few details (right/left foot). The solution to this problem is the possibility to create a hierarchy of IoC containers. The most general components are registered in the root container, while more and more detailed ones - in successive child containers. Searching for a component starts with the container used by its client, then bubbles up the tree. In this way it is possible to shadow the definition of a component by another definition in a container placed lower in the hierarchy.

 

An example of such a hierarchy of containers is shown in the figure below. The following components are registered in the root container: a Comp component, depending on a component Dep of type IDep. The main container also have two children, each of which is registered with a different version of an IDep component. If looking up for a Comp component in the root container, the returned instance will have been injected with the dependency Dep. However, looking up a Comp component in the child container A or B will return an instance injected with DepA and DepB, respectively. Indeed, the child containers will delegate the search for a Comp component to their parent. However, this component IDep dependency will then be resolved locally, resulting with different implementations. The definitions of DepA and DepB are thus said to shadow the definition of Dep from the parent container.  

Below is an example of such strategy shadowing:

<strategy name=„defaultWeapon" class=„com.myapp.BareHand" />

<agent name=„conscripts" class=„com.myapp.Fighter„ />
<agent name=„warriors" class=„com.myapp.Fighter">
  <strategy name=„weapon" class=„com.myapp.BroadSword" />
</agent>
<agent name=„archers" class=„ com.myapp.Fighter ">
  <strategy name=„weapon" class="com.myapp.LongBow" />
</agent>

Injecting dependencies and autowiring components

The dependencies of a component (see component dependencies) can be resolved explicitly by the user, by specifying them in a configuration file (see XML Configuration) or implicitly by the container. In the latter case, the container will make a best effort try to find a corresponding component. If a dependency can't be unambiguously resolved, the container will raise an error.

In both cases the container will inject component instances with proper references to other components.

It sometimes happen that component dependencies form a cycle, i.e. component A depends on B, B depends on C and C depends on A. The IoC container used in AgE does currently not support any kind of such circular dependencies and will raise an error if some are found. This problem can be solved by acquiring a reference to the container itself (see interface injection) and manually resolving the dependency in an initialization method (see below).

Components Lifecycle

An IoC container does not only handle instantiation and dependency injection, but should also manage the lifecycle of its components.

The lifecycle management possibilities of AgE are however still under development.

The current state of work is as follows: 

  • Component instances are lazy-instantiated, that is they will only be created when first accessed. This is particularly important for singleton components, which will not be created if not first accessed.
  • AgE provides a IStatefullComponent interface with init() and finish() callback methods. The container will discover such components, pre-instantiate them and call callback methods at container startup and shutdown. 

    public class MyComponent implements IStatefulComponent {
       @Override
       public void init() {...}
    
       @Override
       public void finish() {...}
    }
  • Such components, however, currently need to be defined in the root container.

Attachments: