Dependency Injection

A ComponentSystem is not able to do much on its own except sending an receiving events. If it needs access to further logic, for example to modify blocks, has to use additional services from the engine.

Inject an Instance

The engine provides a mechanism for field injection to component systems. One example to obtain an instance of a BlockManager could look like this:

@RegisterSystem(RegisterMode.ALWAYS)
public class MySystem extends BaseComponentSystem {

  @In
  private BlockManager blockManager;
}

When a component system is created, all fields marked with the @In annotation will be injected. The fields do not have to be public, it is even recommended to mark them as private.

Warning

  1. Injected fields are null when the class is created, so don’t access them in the constructor.
  2. Other systems may not be initialized before ComponentSystem.initialise() is called. It is not guaranteed, that other systems are already initialized when they are accessed in the initialize() method.

Share a Class

It is also possible to make own implementations available for dependency injection. A system marked with the @Share annotation is registered to be injected in other classes.

Note

It is highly recommended to share an interface instead of the exact class.

One example to provide a new service:

public interface MyService {

    void doSomething();
}

A component system implements this interface and is marked with the @Share annotation, using the interface type as value:

@Share(MyService.class)
@RegisterSystem(RegisterMode.ALWAYS)
public class MyServiceSystem extends BaseComponentSystem implements MyService {

    @Override
    public void doSomething() {
        //...
    }

}

When another system wants to access MyService, it can add a field for it:

@In
private MyService myService;

When the systems are initialized, this field will have the instance of our MyServiceSystem as value.

How it works

There are two systems to register classes for dependency injection. The CoreRegistry is the older system and provides a static mapping from classes to instances, comparable to a singleton pattern.

The Context does pretty much the same in a non-static way and should be used for every new implementation in the dependency injection layer.

Logic for the actual dependency injection is available in the InjectionHelper. It provides methods to inject fields in a class from a given context or the core registry.

Note

Dependency injection is also available in other parts of Terasology than only component systems. Other examples may be NUI widgets, World Generators and some more.