Dagger 2注入构造函数

时间:2017-04-07 21:57:05

标签: java android dependency-injection constructor dagger-2

我开始在我正在开发的应用程序中使用Dagger 2,但我对Dagger 2如何工作有一些疑问。

我得到了@Provides方法背后的所有逻辑和用于初始化依赖关系的@Inject批注,但@Inject批注对类构造函数有点不好意思。

例如:

我是我的应用程序,我有一个模块定义,ContextModule,用于检索我的应用程序的上下文:

ContextModule.java

@Module
public class ContextModule {

    private final Context context;

    public ContextModule(Context context) {
        this.context = context;
    }

    @Provides
    public Context context() {
        return this.context;
    }
}

我的BaseActivityComponent使用此模块:

BaseActivityComponent.java

@BaseActivityScope
@Component(modules = ContextModule.class)
public interface BaseActivityComponent {
    void injectBaseActivity(BaseActivity baseActivity);
}

到目前为止很好..然后我有一个AuthController类,这取决于上下文,我想在我的BaseActivity中注入它。所以在我的AuthControllers.class中我有类似的东西:

public class AuthController {

    private Context context;

    @Inject
    public AuthController(Context context) {
        this.context = context;
    }

    public void auth() {
        // DO STUFF WITH CONTEXT
    }
}

我将它注入我的BaseActivity,如:

public class BaseActivity extends AppCompatActivity {

    @Inject
    AuthController authController;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        BaseActivityComponent component = DaggerBaseActivityComponent.builder()
            .contextModule(new ContextModule(this))
            .build();

        component.injectBaseActivity(this);

        authController.auth();

    }
}

现在我的问题是,dagger如何知道我的AuthControllers是BaseActivity的依赖?只是声明

@Inject
AuthController authController;

就像我创建一个类似于:

的ControllerModule一样
@Module(includes = ContextModule.class)
public class ControllerModule {

    @Provides
    AuthController authController(Context context) {
        return new AuthController(context);
    }

}

然后在我的BaseActivityComponent中,我将添加我的AuthController getter并将我的依赖模块更改为ControllersModule:

@BaseActivityScope
@Component(modules = ControllersModule.class)
public interface BaseActivityComponent {

    void injectBaseActivity(BaseActivity baseActivity);

    AuthController getAuthController();
}

当我调用injectBaseActivity(this)时,它“告诉”匕首所有@Inject注释都是我的类的依赖项,然后它搜索我的项目以寻找与该类型匹配的@Inject注释构造函数?

我认为关于Dagger 2的一个好处是模块文件可以用作我的依赖关系三的“文档”。但是如果只是在我控制的所有构造函数中添加@Inject,将来不会有点混乱,因为你不知道实际上取决于什么? (我的意思是,你知道什么取决于什么,你只需要浏览很多文件才能真正找到答案)

在构造函数中使用@Inject注释或何时在Modules文件中添加@Provides方法时是否有最佳实践? 我在构造函数中使用@Inject得到了我不需要在模块文件中更改构造函数定义,但是有任何缺点吗?

感谢。

1 个答案:

答案 0 :(得分:35)

  

当我调用injectBaseActivity(this)时,它“告诉”匕首所有@Inject注释都是我的类的依赖项,然后它在我的项目中搜索与该类型匹配的@Inject注释构造函数?

完全。但是当你调用injectBaseActivity时它没有完成,但这一切都发生在编译期间。这是注释处理的一种方式(另一种方法是在运行时使用反射)。

当您构建项目时,您在build.gradle文件中包含的dagger-annotation-processor(作为依赖项)将被调用,其中包含由@Inject注释注释的所有字段,类等的列表,用它构建一个依赖图。然后它解析图形,生成源代码,提供图形上项目的所有依赖项。

injectBaseActivity只执行之前生成的代码,并将所有依赖项分配给对象。它是正确的源代码,您可以阅读和调试。

这是编译步骤的原因 - 简单地说就是性能和验证。 (例如,如果您有一些依赖循环,则会出现编译错误)

  

dagger如何知道我的AuthControllers是BaseActivity的依赖?

@Inject
AuthController authController;

通过注释字段@Inject dagger知道你想要一个AuthController。到现在为止还挺好。现在,dagger将寻找一些方法来提供控制器,在组件内查找它,组件依赖项和组件模块。它还将查看是否可以自己提供类,因为知道有关其构造函数。

如果你没有在任何模块中包含它,dagger如何知道对象构造函数?

@Inject
public AuthController(Context context) { /**/ }

通过使用注释注释构造函数,您还告诉dagger有一个名为AuthController的类,并且您需要一个上下文来实例化它。它与将其添加到模块基本相同。

如果您没有将@Provides注释添加到构造函数的源代码,或者对象需要进一步初始化,则应使用模块@Inject方法。或者在你的情况下...

  

[...]模块文件可以用作我的依赖树[...]

的“文档”

是的,当然你可以这样做。但是随着项目的增长,你将不得不维护很多不必要的代码,因为在构造函数上使用简单的注释也可以做到这一点。

  

在构造函数中使用@Inject注释或何时在Modules文件中添加@Provides方法时是否有最佳实践?

如果您想为不同的上下文提供不同的版本(例如,以两种不同的方式实现接口),还有@Binds注释告诉dagger您希望将哪个类作为实现提供。

除此之外,我相信你应该尽可能使用构造函数注入。如果发生了变化,您不必触及代码的任何其他部分,只需编写的代码就少,因此可以包含错误的地方更少。

Dagger也可以并且通过了解更多来优化很多,如果你实现了不必要的代码,它将不得不使用你引入的开销

当然最终完全取决于你认为最好的。毕竟必须使用你的代码;)