可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Suppose I'm building a car and I have several Brake beans with different implementations
class Car {
@Inject
Car(@BrakeType(value="abs")Brake frontBrake, @BrakeType(value="nonabs")Brake rearBrake) { }
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BrakeType {
String value();
}
interface Brake {}
@BrakeType(value="abs")
class AbsBrakeImpl implements Brake {
@Inject AbsBrakeImpl() {}
}
@BrakeType(value="nonabs")
class BrakeImpl implements Brake {
@Inject BrakeImpl() {}
}
why does my CarModule have to define @Provides for the specific Brake types? Shouldn't the custom annotation type @BrakeType be enough to determine which impl to inject? Or would that require using reflection, which dagger2 does not use?
@Module
public class CarModule {
@Provides @BrakeType("abs")
public Brake absBrake() {
return new AbsBrakeImpl();
}
@Provides @BrakeType("nonabs")
public Brake nonabsBrake() {
return new BrakeImpl();
}
}
回答1:
Dagger doesn't look at qualifier annotations on classes, only on @Provides
or @Binds
methods. So the @BrakeType(value="abs")
annotations on your classes don't have any effect.
A more canonical way of writing your code is:
class AbsBrakeImpl implements Brake {
@Inject AbsBrakeImpl() {}
}
class BrakeImpl implements Brake {
@Inject BrakeImpl() {}
}
@Module
abstract class CarModule {
@Binds @BrakeType("abs")
abstract Brake absBrake(AbsBrakeImpl impl);
@Binds @BrakeType("nonabs")
abstract Brake nonabsBrake(BrakeImpl impl);
}
Note that since you have @Inject
on the constructors of your implementations, you can simply use Dagger's @Bind
to bind the implementations directly to the appropriately qualified interface.
回答2:
For the record, you can use your own qualifier annotations (as BrakeType), or just use @Named from Dagger.
Using this last one, your code will look something like:
@Inject @Named("abs") Brake frontBrake;
@Inject @Named("nonabs") Brake rearBrake;
And on your module:
@Provides @Named("abs") static Brake provideAbsBrake() {
return new AbsBrakeImpl();
}
@Provides @Named("nonabs") static Brake provideNonAbsBrake() {
return new BrakeImpl();
}
Remember to use Dagger name conventions (like provide prefix) to get most of it. And on your modules try to use all @Provides methods static, doing so the resultant implementation does not need to instantiate it.
In short, Provides and Qualifiers work together so you need both.
Source: Dagger users guide.
回答3:
@Inject constructor
means provide a type that class itself, in your case, which mean you provide the AbsBrakeImpl
type and BrakeImpl
type, so when you try to inject with Brake
, dagger can not found the provider.
Qualifier in @Inject constructor
is not work, because class type is unique, we don't need to add a qualifier to.
So, in your case, or you have to use CarModule
to tell Dagger explicitly, or change your constructor with
class Car {
@Inject
Car(AbsBrakeImpl frontBrake, BrakeImpl rearBrake) { }
}
回答4:
Reflection is probably not a big issue here because it would happen at compile time.
I did not look through the source code, but dagger is but an annotation processor—it registers to be called whenever a set of given annotations is used. While the qualifier alone would probably be enough to find out what you intended, I can think of the following reasons why this could not be the best solution.
javax.inject.Qualifier
is part of a bigger API, and might also be used by other libraries in different context. So you might not want dagger to generate code for a method, just because it is annotated with a qualifier.
Another reason could be that since there is the possibility to create custom qualifiers, dagger would have to check every annotation on every method in every module and then in turn determine whether that annotation itself is annotated with @Qualifier
to see if the method is of some interest to it. This is rather an unnecessary overhead.
There might be more reasons, but those 2 listed here seem enough to just make users of dagger use some sort of contract: @Provides
.
Annotations don't affect the performance of the code, and having an addtional annotation won't do any harm, so there is more to gain than to lose by handling it the way they do.