当前位置: 首页 > 科技观察

Dagger2——难以置信,不过我好爱你

时间:2023-03-17 12:18:35 科技观察

突破了今天来说说一个稍微冷门的库Dagger。我做一个不负责任的猜测:做client的同学可能听得比较少一些术语,比如面向方面编程,控制反转,依赖注入。好玩的东西。今天我们就来介绍这款依赖注入器——Dagger2,源自Google开发的Square'sDagger,基于apt生成静态编译期依赖注入工具,比动态注入性能更高,但需要更多的约定。官网:https://google.github.io/dagger/Dagger2(以下简称Dagger)主要由两部分组成:Component和Module。它分别作为注入器和注入源存在于整个依赖图中,然后就有了源和工具,所以我们只需要在需要注入的地方加上@Inject注解即可。它是JSR-330的一部分,这里直接介绍最简单的Demo。Module@ModulepublicclassAppModule{ContextmApplicationContext;publicAppModule(Contextcontext){this.mApplicationContext=context;}@ProvidespublicContextprovideContext(){returnmApplicationContext;}@ProvidespublicServiceprovideService(Contextcontext){returnnewService(context,null);}}这就是世界的起源。@Module注解表明这个类是一个模块和一个“源”。@Provides注解告诉Dagger我们要构造对象并提供这些依赖项。Component@Component(modules=AppModule.class)publicinterfaceAppComponent{voidinject(Appapp);Contextcontext();Serviceservice();}Component是一个接口,具体实现是Dagger通过apt工具给你生成的(有没有特殊的if你有aptCool),我们已经将药水分配给AppComponent的“针”——AppModule,并告诉它在注入时从AppModule中获取我们想要的变量实例,只需为Component声明一个无返回值,注入的类型参数方法,Dagger会为这个类生成一个MemberInjector对象,用于向被注入的类中注入对象。注入对象publicclassAppextendsApplication{@InjectServicemService;privateAppComponentmAppComponent;/***应用初始化*/@OverridepublicvoidonCreate(){super.onCreate();mAppComponent=DaggerAppComponent.builder().appModule(newAppModule(this)).build();mAppComponent.inject(this);}publicAppComponentgetAppComponent(){returnmAppComponent;}}这里的DaggerAppComponent是Dagger生成的,它的实现类会在所有Component接口类前加一个Dagger前缀。我们只需要传入它需要的依赖就可以了没错,Component显然依赖于Module,所以这里需要传入AppModule。现在,我们只要使用Context,就可以获得这个AppComponent实例。只要我们有这个实例,我们就可以在任何地方注入托管类。作用域当我们谈到依赖注入时,作用域(Scope)是一个经常出现在我们眼中的词汇。控制一个变量的生命周期,其实质就是控制它存在的范围。服务器的典型作用域是Singleton、Request、Session等,它们的变量存在于不同的生命周期。默认情况下,我们拥有的是Singleton,即@Singleton注解。由它标记的Provider生成的对象会被缓存并用SingleCheck或DoubleCheck包装。我们的Provider指定的范围需要和Component的范围保持一致。例如,Component是这样定义的:@Singleton@Component(modules=AppModule.class)publicinterfaceAppComponent{voidinject(Appapp);Serviceservice();}和Module是这样的@ModulepublicclassAppModule{ContextmApplicationContext;publicAppModule(Contextcontext){this.mApplicationContext=context;}@Singleton@ProvidespublicContextprovideService(){returnnewService();}}QualifierDagger也支持使用限定符(Qualifiers)来指定注入的对象,比如内置的@Named限定符,当我们需要具有特定限定符的变量时names,我们可以在@Inject上,指定@Named限定符来获取指定的对象。//Module@Providers@Named("cache")publicServiceprovideService();//Injection@Inject@Named("cache")ServicemService;这样,一个名为“cache”的实例被注入到这个mService中。应用到一个简单的场景当我们谈论依赖注入时,我们在谈论什么?实际上我们在谈论范围。这是什么意思?相信每个程序员实现单例都是非常简单的,makeInstance和getInstance就可以了。然而,你有想过维护“双实例”吗?我遇到过这样一个场景:一个用户(User)需要一个标签表,将增删改查,关联到用户。问题(Question)还需要一个标签表,同样会进行增删查改,并与问题关联。两个表的实体模型是相同的。那么我们有两个选择,双表,或者双数据库。双表对ORM很不友好,因为ORM是根据类来定表的。为了代码简洁优雅,不可能创建两个相同的类,否则命名它们将成为一件困难的事情。这里,一个好处是我首先使用的ORM库维护了一个单例,单例进行CRUD操作,单例与一个数据库相关。所以我利用Qualifer的特性,生成了两个实例(对应两个数据库),分别注入到不同的业务模型中,这样就可以使用同一个类,对标签修改完全没有影响。如果我们自己来做,我们可能要写很多脏代码,但是Dagger只需要2个注解就解决了我的需求。综上所述,本文简要介绍了如何开始使用Dagger。综上所述,我们只需要约定好Componenet和Module,配合Inject使用,就可以实现一个静态的依赖注入过程。下次我们会详细介绍Dagger生成的代码结构。