It looks like that you are overusing a notion of "dependency". You should separate classes which encapsulate user input (in fact, any data) from classes containing business logic first, and then you should pass these data via methods, not through injection, because user input is not a dependency.
This way you will almost never need assisted injection, because you can create "data" classes directly with new
(it is OK because they have no dependencies), and "behavior" classes can be injected into each other via constructor in standard way. Then "behavior" classes will do they work, passing around objects of "data" classes, not getting them as dependencies. You will see that the need for assisted inject disappears, and your program becomes much more simple and understandable.
For example, instead of having something like the following:
public class OtherDependency {
private final int s;
@Inject
OtherDependency(@Assisted int s, ...) {
this.s = s;
...
}
public void doWork() { /* use s */ ... }
}
public class SumService {
private final int x1;
private final int x2;
private final OtherDependencyFactory depFactory;
@Inject
SumService(@Assisted int x1, @Assisted int x2, OtherDependencyFactory depFactory) {
this.x1 = x1;
this.x2 = x2;
this.depFactory = depFactory;
}
public void doWork() {
int s = x1 + x2;
OtherDependency dep = depFactory.create(s);
dep.doWork();
}
}
public class EntryPoint {
private final SumServiceFactory sumServiceFactory;
@Inject
EntryPoint(SumServiceFactory sumServiceFactory) {
this.sumServiceFactory = sumServiceFactory;
}
public void start() {
Scanner sc = new Scanner(System.in);
int x1 = sc.nextInt();
int x2 = sc.nextInt();
SumService sumService = sumServiceFactory.create(x1, x2);
sumService.doWork();
}
}
(I don't know much about your program, obviously, but this is what I have thought of when I have seen "chain of dependencies which need user input first")
you should make something like
public class OtherDependency {
@Inject
OtherDependency(...) {
...
}
public void doWork(int s) { /* use s */ ... }
}
public class SumService {
private final OtherDependency dep;
@Inject
SumService(OtherDependency dep) {
this.dep = dep;
}
public void doWork(int x1, int x2) {
int s = x1 + x2;
dep.doWork(s);
}
}
public class EntryPoint {
private final SumService sumService;
@Inject
EntryPoint(SumService sumService) {
this.sumService = sumService;
}
public void start() {
Scanner sc = new Scanner(System.in);
int x1 = sc.nextInt();
int x2 = sc.nextInt();
sumService.doWork(x1, x2);
}
}
All user input is transferred from class to class via method parameters, and these classes themselves are stateless and are just normally injected. No need to use assisted inject at all.
Update
I have read your update. There are multiple possibilities to do what you want, with varying complexity.
First, one of the most simple variants (and I'd argue the best one in these circumstances) is, again, to pass your input data via method calls. I do not see how your architecture forbids this. Just make all methods on KnowledgeBaseService
which need this data accept it, and pass it from InfrastructureService
. You do not need assisted inject here.
Second, you can acquire your user input before creating an injector, and then do toInstance()
bind. This depends on the actual architecture and may not work because of it. I believe this is the easiest approach which give very simple and flexible structure.
Third, you can use providers. Create a provider for your ConfigDataObject
which asks for user input and creates corresponding instance, then bind ConfigDataObject
to it in Singleton
scope. This will work if user input does not fail, because you should not throw exceptions from providers. If you need user input validation, use throwing providers extension. User input will then be triggered upon the first access to the provider, and then due to its scope the result will be cached.
There may be other ways, but these are which I was able to come up with.