Dagger2 custom @Qualifier usage

2019-04-21 01:01发布

问题:

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.



标签: java dagger-2