我遇到了一个实例,其中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()
stacktrace的代码:
//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();
}
答案 0 :(得分:1)
Lazy<T>
并不意味着“稍后确定T是否已绑定”,它的意思是“确保在编译时T存在绑定,而仅在我调用get
之后在运行时创建实例”。在所有情况下,您仍然需要使T的绑定可用,但是Dagger在明确要求它之前不会尝试创建它的实例。
Dagger要求对于Provider<T>
和Lazy<T>
的所有使用,绑定T必须在编译时存在,即使在运行时不调用它也是如此。这样可以确保,如果您确实在Provider或Lazy实例上调用get()
,则它不会在运行时因其知道在编译时丢失的绑定而失败。 (Lazy的行为与Provider完全相同,只是Lazy会记住它返回的实例,无论绑定是否作用域。)
这意味着您的一个选择是为ModelMqlBased添加一个绑定,该绑定返回null
或抛出异常,这在Dagger中通常是一个可怕的主意,但是对于您在运行时知道永不调用Provides方法的情况足够了。
使用@BindsOptionalOf是获得所需灵活性的另一种方法。这样,您就可以注入Optional<T>
或Optional<Lazy<T>>
,如果绑定存在,则解析为当前值;如果绑定不存在,则解析为当前值。
@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不会抱怨重复。