I'm learning Dagger. I'm a former Spring user. One of the things I noticed is I have to annotate each bean with @IntoSet
to inject beans of a certain type as a collection. In Spring, I'd directly declare, for example, Set<MyBeanClass>
as a parameter in an autowired method or constructor, and the framework would figure all out itself (granted, "framework figuring things out for you" is not always an advantage)
However, @IntoSet
sometimes breaks things. Here's an MRE:
package myown;
import dagger.Component;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.Set;
@Component(modules = {PersonModule.class})
@Singleton
public interface PersonComponent {
Person getJane();
}
package myown;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;
import javax.inject.Singleton;
@Module(includes = PetModule.class)
public class PersonModule {
@IntoSet
@Singleton
@Provides
public Person jane(Pet pet) {
return new PetOwner("Jane", pet);
}
}
package myown;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
@Module
public class PetModule {
@Provides
@Singleton
Pet getPet() {
return new Pet("Puffy");
}
}
package myown;
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
}
package myown;
public class PetOwner extends Person {
private final Pet pet;
public PetOwner(String name, Pet pet) {
super(name);
this.pet = pet;
}
}
package myown;
public class Pet {
private final String name;
public Pet(String name) {
this.name = name;
}
}
Try to build it and you get:
java: [Dagger/MissingBinding] myown.Person cannot be provided without an @Inject constructor or an @Provides-annotated method.
myown.Person is provided at
myown.PersonComponent.getJane()
However, if you comment out @IntoSet
and try to rebuild it, everything is fine
To be clear, I want jane
to be able to be injected directly and as part of an injected Set
of all Person
s (imagine, there are jack
, john
, etc. as well -- also annotated with @IntoSet
)
Why is it happening, and is there a solution/workaround?
When you provide with @IntoSet it will only be in your Set. You can not directly inject 'Jane'
If you want to inject Jane specifically you can provide Jane with a named annotation or a subtype and then bind or provide it again with @IntoSet
@Module(includes = PetModule.class)
public class PersonModule {
@Singleton
@Provides
@Named("Jane")
public Person jane(Pet pet) {
return new PetOwner("Jane", pet);
@Provides
@IntoSet
public Person janeIntoSet(@Named("Jane") jane: Person) {
return jane;
}
}
There are multiple ways to achieve it. See https://dagger.dev/dev-guide/multibindings.html.
The difference in Spring is that it figures it out at runtime while everything in dagger is compile time generated. So there is no guessing or casting or reflection going on here. It needs to be all clear at compile time.