0

I am using Guice for assisted injection. Here is a standard scenario:

Class TargetType {

   @Inject
   TargetType(@Assisted Param1 assistedParam, InjectedType injectedType) 
   {
      /* ... */
   }
}

Class InjectedType {

   @Inject
   injectedType(OtherInjectedType otherInjectedType) 
   {
      /* ... */
   }
}

Now I could use Guice factory to call TargetTypeFactory.create(assistedArg /* instanceof Param1 */) and happily obtain my instance of TargetType with instance of InjectedType injected by Guice.

My problem is: what if I want for the InjectedType to have reference to the instance of the TargetType being created? In other words, I want:

Class TargetType {

   @Inject
   TargetType(@Assisted Param1 assistedParam, InjectedType injectedType) 
   {

   }
}

Class InjectedType {

   @Inject
   injectedType(/* How to get this? -> */  TargetType constructedTargetType, OtherInjectedType otherInjectedType) 
   {

   }
}

My current workaround is quite ugly: I create a TargetType manually without the InjectedType, then I use InjectedTypeFactory to get the InjectedType instance and call the setInjectedType(InjectedType) method on the TargetType instance. Ungh!

Konrad Jamrozik
  • 3,254
  • 5
  • 29
  • 59
  • I really doubt that this is possible, especially considering that you're introducing a circular dependency, so Guice should also create a proxy object. I might be wrong, however. – Vladimir Matveev Dec 13 '13 at 19:41
  • Forget Guice, how would you expect to do this if you were calling the constructors manually? – Tavian Barnes Dec 13 '13 at 20:23
  • I expect Guice would instantiate a proxy object for me behind the scenes as explained here: http://stackoverflow.com/a/10014950/986533 Of course I wouldn't create such a proxy myself, too much boilerplate code. – Konrad Jamrozik Dec 13 '13 at 21:17

1 Answers1

1

Interesting problem. I don't think that there's a totally clean solution to this, but maybe this solution based on child injectors will work for you:

First, create a custom guice binding annotation for your own use:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
public @interface JamrozikParam {}

Then, change TargetType to look like this:

class TargetType {

   @Inject
   TargetType(@JamrozikParam Param1 assistedParam, InjectedType injectedType) 
   {

   }
}

Leave InjectedType as it was.

Now, this is how you get an instance of TargetType:

Injector topInjector = Guice.createInjector(/* Your modules here */);
// Note that the modules used there can't mention TargetType or InjectedType
// Now many lines later
Param1 myP1val;
myP1val = // ... however that was computed
Injector childInjector = topInjector.childInjector(new Param1Module(myP1val));
TargetType t1 = childInjector.getInstance(TargetType.class);

Where Param1Module is a class that looks like:

public class Param1Module extends AbstractModule {
  private final Param1 p1val;
  public Param1Module(Param1 p1val) {this.p1val = p1val;}
  protected void configure() {
    bind(Param1.class).annotatedWith(JamrozikParam.class).toInstance(p1val);
    bind(TargetType.class).in(Singleton.class);
    bind(InjectedType.class).in(Singleton.class);
  }
}

Note that in order for this to work, Guice is going to be creating a proxy to work around the circular dependency. I suggest that you make its job of proxy creation easier and make InjectedType implement an interface, have TargetType inject that interface instead of injecting InjectedType directly, and then bind that interface to InjectedType. Otherwise, guice will have to do horrible stuff with bytecode rewriting on the fly and that's just asking for trouble.


I strongly urge you to look at your whole problem and refactor things so that you don't need to do this. Maybe your two objects don't need to know about each other in their constructors? For example, maybe you could pass them references to each other as method parameters by going through some supervisor object:

class TargetType {

   @Inject
   TargetType(@Assisted Param1 assistedParam) 
   {

   }

   void ttMethod1(InjectedType injected, String other) { }
   // other methods
}

class InjectedType {

   @Inject
   injectedType(OtherInjectedType otherInjectedType) 
   {

   }

   void injMethod1(TargetType constructedTarget, String other) { }
   // other methods
}

class TargetTypeSupervisor {
  private TargetType target;
  private InjectedType injected;

  @Inject TargetTypeSupervisor(@Assisted Param1 assistedParam, InjectedType injected) {
    this.injected = injected;
    this.target = new Target(assistedParam);
  }

  void ttMethod1(String other) { target.ttMethod1(injected, other); }
  void injMethod1(String other) { injected.injMethod1(target, other); }
  // other methods as needed
}
Daniel Martin
  • 23,083
  • 6
  • 50
  • 70
  • Thanks for a comprehensive answer! My solution is similar to your `Supervisor`: my `TargetType` has a `setInjectedType` method which is called immediately after the `TargetType` constructor, called in the constructor of manually-coded `TargetTypeFactory`. I use interfaces everywhere, but omitted them here for the sake of simplicity. I understand the injector solution requires from me to create the child injector after I obtained `myP1val` which would require calling Guice after some of my app logic is executed. Thus, I will stay with the manual factory solution. Thanks again! :) – Konrad Jamrozik Dec 14 '13 at 17:54