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
}