Java 如何使用Dagger-2惰性注入接口?

Java 如何使用Dagger-2惰性注入接口?,java,dependency-injection,dagger-2,Java,Dependency Injection,Dagger 2,我遇到了一个Dagger-2不允许我注射的例子。似乎它仍然需要我在编译时提供对象。为什么呢 堆栈跟踪: [Dagger/MissingBinding] @javax.inject.Named("htfModel") de.wimj.core.Applications.IModel cannot be provided without an @Provides-annotated method. [ERROR] @javax.inject.Named("htfModel") de.wi

我遇到了一个Dagger-2不允许我注射的例子。似乎它仍然需要我在编译时提供对象。为什么呢

堆栈跟踪:

[Dagger/MissingBinding] @javax.inject.Named("htfModel") de.wimj.core.Applications.IModel cannot be provided without an @Provides-annotated method.
[ERROR]       @javax.inject.Named("htfModel") de.wimj.core.Applications.IModel is injected at
[ERROR]           de.wimj.ui.Mt5Painter.<init>(…, htfTradeModel, …)
[ERROR]       dagger.Lazy<de.wimj.ui.Mt5Painter> is injected at
[ERROR]           de.wimj.core.Applications.ModelMqlBased.<init>(…, mt5Painter, …)
[ERROR]       dagger.Lazy<de.wimj.core.Applications.ModelMqlBased> is injected at
[ERROR]           de.wimj.di.components.trademodel.ModelModule.iModel(modelMqlBased, …)
[ERROR]       de.wimj.core.Applications.IModel is provided at
[ERROR]           de.wimj.di.components.trademodel.ModelComponent.createModel()
//Got it, Dagger-2 wants me to provide a IModel here
@ModelScope
@Component(modules = { ModelModule.class }, dependencies = { ClientComponent.class })
public interface ModelComponent {

    IModel createModel();

    @Component.Builder
    interface Builder {
        ModelComponent build();
        Builder clientComponent(ClientComponent clientComponent); //MT5Trader comes from this component
    }

}


//At this point I will provide the IModel. I do NOT get, why Dagger-2 forces
//me to provide a "ModelMqlBased" though. I obviously lazy-inject it. 
//I used this pattern in other cases as well (providing an interface and 
//lazy-injecting the possible instantiations as params)
@Module
public class ModelModule {

    @Provides
    @ModelScope
    IModel iModel(  Lazy<ModelMqlBased> modelMqlBased,  //lazy-injection here!
            ModelFileBased modelFileBased,
            @Named("configClientType")String clientType) {
        switch (clientType) {
        case "mqlBot": 
            return modelMqlBased.get();
        case "fileBot":
                return modelFileBased;
        default:
            throw new RuntimeException();
        }
    }
}

stacktrace的代码:

[Dagger/MissingBinding] @javax.inject.Named("htfModel") de.wimj.core.Applications.IModel cannot be provided without an @Provides-annotated method.
[ERROR]       @javax.inject.Named("htfModel") de.wimj.core.Applications.IModel is injected at
[ERROR]           de.wimj.ui.Mt5Painter.<init>(…, htfTradeModel, …)
[ERROR]       dagger.Lazy<de.wimj.ui.Mt5Painter> is injected at
[ERROR]           de.wimj.core.Applications.ModelMqlBased.<init>(…, mt5Painter, …)
[ERROR]       dagger.Lazy<de.wimj.core.Applications.ModelMqlBased> is injected at
[ERROR]           de.wimj.di.components.trademodel.ModelModule.iModel(modelMqlBased, …)
[ERROR]       de.wimj.core.Applications.IModel is provided at
[ERROR]           de.wimj.di.components.trademodel.ModelComponent.createModel()
//Got it, Dagger-2 wants me to provide a IModel here
@ModelScope
@Component(modules = { ModelModule.class }, dependencies = { ClientComponent.class })
public interface ModelComponent {

    IModel createModel();

    @Component.Builder
    interface Builder {
        ModelComponent build();
        Builder clientComponent(ClientComponent clientComponent); //MT5Trader comes from this component
    }

}


//At this point I will provide the IModel. I do NOT get, why Dagger-2 forces
//me to provide a "ModelMqlBased" though. I obviously lazy-inject it. 
//I used this pattern in other cases as well (providing an interface and 
//lazy-injecting the possible instantiations as params)
@Module
public class ModelModule {

    @Provides
    @ModelScope
    IModel iModel(  Lazy<ModelMqlBased> modelMqlBased,  //lazy-injection here!
            ModelFileBased modelFileBased,
            @Named("configClientType")String clientType) {
        switch (clientType) {
        case "mqlBot": 
            return modelMqlBased.get();
        case "fileBot":
                return modelFileBased;
        default:
            throw new RuntimeException();
        }
    }
}

以下代码应该是无关的,关键是ModelModule,但为了完成:

@ModelScope
public class ModelMqlBased implements IModel {

    @Inject
    public ModelMqlBased( Lazy<Mt5Painter> mt5Painter) {
        super();
        this.mt5Painter = mt5Painter.get();
    }

}

//this one sits in a "higher-scoped" component
@ClientScope
public class Mt5Painter {

    private IModel htfModel;
    private IModel ltfModel;

    @Inject
    public Mt5Painter(@Named("htfModel") Lazy<IModel> htfTradeModel, @Named("ltfModel") Lazy<IModel> ltfTradeModel) {
        super();
        this.htfModel = htfTradeModel.get();
        this.ltfModel = ltfTradeModel.get();
    }
Lazy并不意味着稍后确定t是否绑定,它意味着确保在编译时存在t的绑定,而只是在调用get之后在运行时创建一个实例。您仍然需要使T的绑定在所有情况下都可用,但Dagger不会尝试创建它的实例,除非您明确要求它

Dagger要求,对于Provider和Lazy的所有使用,绑定T需要在编译时存在,即使在运行时您不调用它。这确保了如果在提供程序或惰性实例上调用get,它在运行时不会因为编译时知道缺少的绑定而失败。Lazy的行为与提供程序的行为完全相同,只是Lazy会记住它返回的实例,而不管绑定的作用域是否限定

这意味着,您的选项之一是为ModelMqlBased添加一个返回null或引发异常的绑定,这在Dagger中通常是一个糟糕的想法,但对于在运行时知道从未调用过提供的方法的情况,这就足够了

实现您所期望的灵活性的另一种方法是使用。这允许您插入可选或可选的,如果绑定存在,则解析为当前值;如果绑定不存在,则解析为不存在的占位符

@Module
public abstract class ModelModule {
    // Note abstract class and static/abstract methods.

    @BindsOptionalOf
    abstract ModelMqlBased bindOptionalOfModelMqlBased();

    @Provides
    @ModelScope
    static IModel iModel(Optional<ModelMqlBased> modelMqlBased,
            ModelFileBased modelFileBased,
            @Named("configClientType")String clientType) {
        switch (clientType) {
        case "mqlBot": 
            return modelMqlBased.get();
        case "fileBot":
            return modelFileBased;
        default:
            throw new RuntimeException();
        }
    }
}

这可能使重用模块变得更容易,特别是因为与多绑定一样,您可以提供尽可能多的@BindsOptionalOf abstract T bindOptionalOfT;方法如您所愿,Dagger不会抱怨重复。

当使用@BindsOptionalOf时,如何自动连接ModelMqlBased?我是否需要在方法内部手动将其连接在一起。显然,modelMqlBased.get总是会导致NoSuchElementException,因为永远不会有值,对吗?@cobby@BindsOptionalOf最适合于具有可重用模块的情况,其中有问题的绑定可能存在于其他模块中,也可能不存在于其他模块中。如果您只知道在运行时绑定是否必要,那么Dagger在所有情况下都需要@Providers方法,因为它不会让您请求它不知道如何提供的绑定。注入Lazy就等于请求绑定T,即使你只是在很久以后才请求T的一个实例。嗯,那么看来Lazy和BindsOptionalOf都不能解决我的问题。在本例中,我需要基于配置标志创建ModelMqlBased或ModelFileBased。ModelModule应在两个组件中使用,一个组件提供ModelMqlBased的依赖项,另一个组件通过组件依赖项提供ModelFileBased的依赖项。我认为最好使用一个模块并基于该配置标志返回所需的实例化。显然,我需要为每个组件拆分模块。是这样吗?如果您致力于基于组件依赖关系的结构,那么我认为这是正确的。我仍然支持注入依赖项解决方案,我看到了您的评论。这种类型的解决方案允许您在运行时进行选择,而无需组件依赖项的开销,也无需创建不必要的实例,但您必须将组件配置为能够同时提供ModelMqlBased和ModelFileBased。不过,所有这些都偏离了您最初提出的关于懒惰的问题。您的文章中的哪个解决方案是“组件依赖性”标题下的解决方案?