Reputation: 2299
I am trying to set up the following in Dagger 2:
An EventFinder
has two parts: a TriggerFinder
and an ArgFinder
each of which have many different implementations which are themselves complex objects with many dependencies. The code sample below successfully builds an EventFinder
with sample TriggerFinder
and ArgFinder
which both depend on another object, WordNet
. However, WordNet
is a very big, expensive, constant object I would like to share between models as a singleton. The current implementation of WithWordNetEventFinderComponent
loads up WordNet
twice. If I try marking the @Provides
method in WordNetModule
as @Singleton
, I get the error:
Error: ArgFinderUsingWordNetComponent (unscoped) may not reference scoped bindings:
@Singleton @Provides WordNet WordNetModule.provideWordNet()
But propagating @Singleton
annotations up the tree of components only results in other errors. What if the correct way to do this?
Full code:
import org.junit.Test;
import javax.inject.Inject;
import javax.inject.Named;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
// WordNet - this is the expensive shared resource we would
// like to make a singleton
class WordNet {
final String path;
public WordNet(@Named("path") String path) {
System.out.println("Loading Fake wordnet from " + path);
this.path = path;
}
}
@Module
class WordNetModule {
final String path;
public WordNetModule(String path) {
this.path = path;
}
// uncommenting the line below causes errors
//@Singleton
@Provides
public WordNet provideWordNet() {
return new WordNet(path);
}
}
interface TriggerFinder {
}
class TriggerFinderUsingWordnet implements TriggerFinder {
WordNet wordNet;
@Inject
public TriggerFinderUsingWordnet(WordNet wordNet) {
this.wordNet = wordNet;
}
}
@Module(includes = WordNetModule.class)
class TriggerFinderWithWordnetModule {
@Provides
public TriggerFinder provideTriggerFinder(TriggerFinderUsingWordnet triggerFinder) {
return triggerFinder;
}
}
interface ArgFinder {
}
class ArgFinderUsingWordnet implements ArgFinder {
WordNet wordNet;
@Inject
public ArgFinderUsingWordnet(WordNet fakeWordNet) {
this.wordNet = fakeWordNet;
}
}
@Module(includes = WordNetModule.class)
class ArgFinderWithWordNetModule {
@Provides
public ArgFinder provideArgFinder(ArgFinderUsingWordnet argFinder) {
return argFinder;
}
}
// the composite object we wish to create
class EventFinder {
private final TriggerFinder triggerFinder;
private final ArgFinder argFinder;
@Inject
public EventFinder(TriggerFinder triggerFinder, ArgFinder argFinder) {
this.triggerFinder = triggerFinder;
this.argFinder = argFinder;
}
}
// components to wire everything together
interface ArgFinderComponent {
ArgFinder argFinder();
}
interface TriggerFinderComponent {
TriggerFinder triggerFinder();
}
@Component(modules = ArgFinderWithWordNetModule.class)
interface ArgFinderUsingWordNetComponent extends ArgFinderComponent {
}
@Component(modules = TriggerFinderWithWordnetModule.class)
interface TriggerFinderUsingWordNetComponent extends TriggerFinderComponent {
}
interface EventFinderComponent {
EventFinder eventFinder();
}
@Component(dependencies = {ArgFinderUsingWordNetComponent.class,
TriggerFinderUsingWordNetComponent.class})
interface WithWordNetEventFinderComponent extends EventFinderComponent {
}
public class DaggerComponentTest {
@Test
public void withWordNetTest() {
final WordNetModule wordNetModule = new WordNetModule("myPath");
DaggerWithWordNetEventFinderComponent.builder()
.argFinderUsingWordNetComponent(
DaggerArgFinderUsingWordNetComponent.builder().wordNetModule(wordNetModule).build())
.triggerFinderUsingWordNetComponent(
DaggerTriggerFinderUsingWordNetComponent.builder().wordNetModule(wordNetModule).build())
.build().eventFinder();
}
}
Upvotes: 1
Views: 2267
Reputation: 13600
You usually mark the top component as @Singleton
and then all the subcomponents and provide*()
that need it. After all, singleton can be guaranteed only on global graph level.
Starting to mark with @Singleton
from bottom will lead to multiple errors (as you experienced) until you get to the very top. So probably it will be easier to rollback and start from the top @Component
Upvotes: 3