Search code examples
fantom

Why afIoc doesn't inject the service in this case?


I'm trying to create a very simple app using the IoC framework for Fantom afIoc to become familiar with it. I tried this...

using afIoc

class Main {
  Registry registry := IocService([AppModule#]).start.registry

  @Inject
  myPod::Version? version

  Void main() {
    echo("version is $version")
  }    
}

The Version class is

const class Version {
  override Str toStr() {
    "0.0.1"
  }
}

The AppModule is

using afIoc

class AppModule {
  static Void bind(ServiceBinder binder) {
    binder.bind(myPod::Version#)
  }
}

It compiles but prints version is null. I fixed the problem by redefining my Main class:

using afIoc

class Main {

  Registry registry := IocService([AppModule#]).start.registry

  Void main() {
    version := (myPod::Version) registry.serviceById("myPod::Version")
    echo("version is $version")
  }
}

But I'd like to understand the lifecycle of the afIoc Registry and why the Version service is not injected in my first version of the Main class. Can anyone explain, please?


Solution

  • I've seen people ask similar questions of other IoC frameworks... So lets look at what happens when you run the program:

    using afIoc
    
    class Main {  //................................................... 1
      Registry registry := IocService([AppModule#]).start.registry //.. 2
    
      @Inject
      myPod::Version? version //....................................... 3
    
      Void main() {
        echo("version is $version") //................................. 4
      }    
    }
    
    1. Fantom instantiates the Main class.

    2. Fantom creates the registry field and assigns it to the result of IocService([AppModule#]).start.registry. This statement simply builds and returns the IoC registry.

    3. Fantom creates the version fields and defaults it to null.

    4. Fantom calls the main() method which prints out the version, which is null.

    Note that nowhere above have we asked IoC to meddle with our Main class. We just new'ed up the IoC Registry and set it to a field.

    If we want the IoC to inject values into a class we have to ask it to:

      Void main() {
        registry.injectIntoFields(this)
    
        echo("version is $version")  // --> version is 0.0.1
      }
    

    Or we could ask IoC to instantiate a new instance of Main for us:

    registry.autobuild(Main#)
    

    Note that many calls to autobuild() would create many instances of Main.

    For IoC to only create one instance of Main (a singleton), define it as a service in AppModule and use regsitry.serviceById() or registry.dependencyByType() - much as you discovered with Version.