How to change an injected instance?

Chris's Avatar

Chris

09 Oct, 2014 05:38 AM

If I say:-

injector.map(MyClass).asSingleton();
and then use it
[Inject]
public var myClass:MyClass;
how do I change the instance of myClass to a new and different instance and let all other classes that inject it know it has changed?
  1. Support Staff 1 Posted by Ondina D.F. on 09 Oct, 2014 10:42 AM

    Ondina D.F.'s Avatar

    Hi Chris,

    As far as I know, that's not possible. The [Inject] is not dynamic. The injector will satisfy the dependencies of a class only when that class is instantiated.
    Perhaps you're after something like the lookup method injection in Java, but that uses bytecode generation, which robotlegs doesn't provide.

    I think that you'll have to create your own 'lookup' mechanism or use a class factory of your own. Or, you can create a factory that uses the Injector to create and retrieve new instances of a class, say SomeClass. You'd also need to unmap and remap SomeClass, when you create new instances.
    Have getters and setters for someClass inside the factory.
    Then you can inject the factory wherever you need SomeClass, and get it from the factory.

    Would that work for you?

    Ondina

  2. 2 Posted by Chris on 09 Oct, 2014 12:29 PM

    Chris's Avatar

    Would this work?:-

    // in the config file
    injector.map(MyClass).asSingleton();
    
    and then:-
    // elsewhere when I need to set the instance
    var myClass:MyClass = new MyClass();
    injector.map(MyClass).toValue(myClass);
    injector.injectInto(myClass);
    
    Does injector.map remap and if so what event gets triggered when this happens? Is it MappingEvent.POST_MAPPING_CHANGE?

    You mentioned unmap so alternatively:-

    //  elsewhere when I need to set the instance
    var myClass:MyClass = new MyClass();
    injector.unmap(MyClass);
    injector.map(MyClass).toValue(myClass);
    injector.injectInto(myClass);
    
    In this case, would I need to listen for Mapping.POST_MAPPING_REMOVE and MappingEvent.POST_MAPPING_CREATE?
  3. Support Staff 3 Posted by Ondina D.F. on 09 Oct, 2014 12:47 PM

    Ondina D.F.'s Avatar

    The second case is ok.

    In the factory method that creates the new instance (setter) you first unmap the previously mapped instance in your config, then create a rule(map) for the new instance, like you did.
    In the factory method that you'll use to retrieve (getter) the new instance :

    _myClass = injector.getInstance(MyClass);
    return _ myClass;

    No need to listen to those framework events.

  4. Support Staff 4 Posted by Ondina D.F. on 09 Oct, 2014 01:03 PM

    Ondina D.F.'s Avatar

    If you're asking yourself why you need to unmap the class before mapping it again, just try it out without the line of code that unmaps the class, and you'll get a warning coming from InjectionMapping. toProvider()(look at the method's comments) and an error from the VigilanceExtension. mappingOverrideHandler() like these:

    Warning: Injector already has a mapping for [class name]
    If you have overridden this mapping intentionally you can use "injector.unmap()" prior to your replacement mapping in order to avoid seeing this message.

    Error: Injector mapping override for type [class ClassName]

  5. 5 Posted by Chris on 09 Oct, 2014 01:10 PM

    Chris's Avatar

    I believe I do need to listen for those framework events because the class that replaces the instance is not the same class(es) the instance is used by/injected into.

    What does inject.injectInto do? Can I skip the remapping and simply re-inject the new instance and listen for the POST_INSTANTIATE mapping event?

  6. Support Staff 6 Posted by Ondina D.F. on 09 Oct, 2014 01:58 PM

    Ondina D.F.'s Avatar
    injectInto(target:Object):void
    
    Inspects the given object and injects into all injection points configured for its class.
    
    target The instance to inject into
    

    I don't quite understand what you're saying. So, let's see if this is correct( used names are silly):

    ClassCreator and ClassRetriever are the 2 classes operating on MyClass.
    ClassCreator creates a new instance of MyClass and a mapping, somewhere at some point in time.
    Also, it uses injector.injectInto(ClassRetriever); in order to inject MyClass into ClassRetriever?

    ClassRetriever is injected into NeedyClass.
    And you want NeedyClass to get MyClass like this: myClass = classRetriever.myClass; ?

    That wouldn't work, because the injector can't dynamically inject MyClass into ClassRetriever. If ClassRetriever was already instantiated by the moment you create a new MyClass inside ClassCreator, injectInto wouldn't do anything.
    We are back at the beginning of this post:)
    Or, I misunderstood you.

    Why can't you use the same class for creating and retrieving new instances of a certain class?

  7. Support Staff 7 Posted by Ondina D.F. on 09 Oct, 2014 04:39 PM

    Ondina D.F.'s Avatar

    In my previous example, after ClassCreator creates a new instance of MyClass and a mapping for it, it also can dispatch a custom event on the shared event dispatcher.

    ClassRetriever, injected with a shared event dispatcher as well, listens to that event, and it uses the injector to get the newly created instance of MyClass, or, if you want, you can send the new instance of MyClass as an event payload. This way you don't need to listen to rl's internal events.

    Regarding injectInto, maybe I wasn't clear enough about its usage.
    You need to use injectInto(myClass) only when you're constructing an instance of MyClass yourself, as it is the case with mapValue() inside the ClassCreator, and only if MyClass has dependencies on its own, that need to be satisfied.

    When you map MyClass the first time in your config .asSingleton(), the injector will be the one instantiating MyClass, and because of this, it will satisfy MyClass' dependencies, thus you don't need an additional .injectInto()

    Hopefully, this time I got it right;)

  8. 8 Posted by dedorris on 10 Oct, 2014 11:18 PM

    dedorris's Avatar

    I think this problem could be resolved using standard Robotlegs goodness, if you approached it differently...

    Your example wants to map a shared instance of MyClass for dependency injection and then swap the instance used while your app is running.

    injector.map(MyClass).asSingleton();
    

    This is the same way you inject a singleton Model or Service.

    injector.map(MyModel).asSingleton();
    

    If you think of your mapping as a Model, the instance that is changing can be a changing property value of that same injected Model class.

    myModel.changingInstance = new MyClass();
    

    You could then use the shared eventDispatcher (or some other variation of the Observer design pattern) to listen for updates to the Model when the MyClass instance changes.

    This way you don't need to map/unmap your instance using the injector.

  9. Support Staff 9 Posted by Ondina D.F. on 13 Oct, 2014 12:09 PM

    Ondina D.F.'s Avatar

    Yes, there are many possible approaches.
    In your example, if you're always accessing the new instance of MyClass through MyModel, there is indeed no need to map/unmap MyClass. But, you'd still need the injector's injectInto(), if MyClass had some dependencies injected that needed to be satisfied. But, of course, you could manually provide MyClass with its dependencies when you create it, as well.

    The unmap/map thing is needed if you want MyClass to be injected ([Inject]) into classes that aren't already instantiated by the time you create new instances of MyClass, or if you want/need to retrieve it using injector.getInstance().
    I don't know why I thought that Chris wanted to have both, the possibility to retrieve the current instance of MyClass in already instantiated classes and to also inject MyClass in other parts of the application, in a Command for example. So, thanks for helping clarify that.

    Then there is the event approach, that we both mentioned.

    Regarding the creation of new instances, I just wanted to remind you of yet other ways of instantiating classes through the Injector, that might be useful in perhaps other scenarios:

    _myClass = injector.instantiateUnmapped(MyClass);
    _myClass = injector.getOrCreateNewInstance(MyClass);

    injector.map(MyClass).asSingleton(true);

    I mentioned those in another discussion:

    http://knowledge.robotlegs.org/discussions/robotlegs-2/11663-how-to...

    Here, the swiftsuspenders methods mentioned above:

    https://github.com/robotlegs/swiftsuspenders/blob/master/src/org/sw...
    https://github.com/robotlegs/swiftsuspenders/blob/master/src/org/sw...
    https://github.com/robotlegs/swiftsuspenders/blob/master/src/org/sw...

  10. Ondina D.F. closed this discussion on 05 Dec, 2014 11:50 AM.

Comments are currently closed for this discussion. You can start a new one.

Keyboard shortcuts

Generic

? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac