只有Inject是不可以的,必须有Component

public class Test {
@Inject Person person; private void test() {
System.out.println(person.name);
} public static void main(String[] args) {
new Test().test();//NullPointerException
}
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";
}
}

可以只有Inject和Component,没有Module

public class Test {
@Inject Person person; private void test() {
DaggerMainComponent.builder().build().inject(this);//必须有注入的代码,否则根本无法将实例注入到目标类中
System.out.println(person.name);
} public static void main(String[] args) {
new Test().test();//默认的名字
System.out.println(new Test().person.name);//NullPointerException。必须有注入的代码
}
}
@Component//可以不指定Module。也可以指定Module但不提供相应的方法
interface MainComponent {
void inject(Test obj);
}
class Person {
public String name;
@Inject
public Person() {
name = "默认的名字";
}
}

同时有Inject和Module时,优先使用Module

Component会首先从Module维度中查找类实例,若找到就用Module维度创建类实例,并停止查找Inject维度,否则才是从Inject维度查找类实例。
所以创建类实例级别:Module维度要高于Inject维度。
public class Test {
@Inject Person person; private void test() {
DaggerMainComponent.builder().mainModule(new MainModule("白乾涛")).build().inject(this);
System.out.println(person.name);
} public static void main(String[] args) {
new Test().test();//白乾涛
}
}
@Component(modules = MainModule.class)//指定Module
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
private String name; public MainModule(String name) {
this.name = name;
} @Provides
Person providerPerson() {
return new Person(name);//调用的是这里
}
}
class Person {
public String name; @Inject
public Person() {
name = "默认的名字";//优先使用Module,所以不会调用这里
} public Person(String name) {
this.name = name;
}
}

递归注解_1_在Module中构造对象时依赖另一个参数

public class Test {
@Inject Person person;//8 private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println(person.name);//9
} public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj); void injectName(MainModule obj);
}
@Module
class MainModule {
@Inject String name;//5 @Provides
Person providerPerson() {//2
DaggerMainComponent.builder().mainModule(new MainModule()).build().injectName(this);//3
return new Person(name);//6
} @Provides
String providerName() {//4
return "包青天";
}
}
//********************************************等价于下面这种形式,更简洁**********************************************
@Module
class MainModule {
@Provides
Person providerPerson(String name) {//2,5
return new Person(name);//6
}
@Provides
String providerName() {//3
System.out.println("即使后面用不到providerPerson方法中的形参name,也会走到这个方法里");
return "包青天";//4
}
}
class Person {
public String name; @Inject
public Person() {
name = "默认的名字";//优先使用Module,所以不会调用这里
} public Person(String name) {//7
this.name = name;
}
}

递归注解_2_在构造方法中构造对象时依赖另一个参数

这个例子不要看,简直不能再乱了!
public class Test {
@Inject Person person;//6 private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println(person.name);//7
} public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj); void injectName(Person obj);
}
@Module
class MainModule {
@Provides
String providerName() {//4
return "包青天";
}
}
class Person {
@Inject public String name;//5 @Inject
public Person() {//2
DaggerMainComponent.builder().mainModule(new MainModule()).build().injectName(this);//3
}
}

递归注解_3_用@Inject注解标注有参构造方法

public class Test {
@Inject Person person;//6 private void test() {
System.out.println("C");
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);//1
System.out.println("F");
System.out.println(person.name);//7
} public static void main(String[] args) {
System.out.println("A");
Test test = new Test();
System.out.println("B");
test.test();//包青天
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
String providerName() {//3
System.out.println("D");//先走这里*************************************
return "包青天";
}
}
class Person {
public String name;//5 @Inject
public Person(String name) {//2
System.out.println("E");//再走这里*************************************
this.name = name;//4
}
}

Qualifier和Named注解,区分用哪个方法创建对象

若一个类的实例有多种方法可以创建出来,那Component应该选择哪种方法来创建该类的实例呢?
@Qualifier和Named注解就是解决依赖注入迷失问题的。dagger2在发现依赖注入迷失时,在编译代码时会报错。
注意:我发现不可以用Qualifier和Named标注构造方法,会报如下错误。
Error:(41, 1) Gradle: 错误: @Qualifier annotations are not allowed on @Inject constructors.
public class Test {
@Inject Person person;
@Inject @MyType(1) Person person1;//将所有@MyType(1)改为@Named("1")也是相同的效果
@Inject @MyType(2) Person person2;//必须能在Module中找到相应的提供对象的方法,否则报错。 private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println(person.name + " " + person1.name + " " + person2.name);//默认的名字 普通人 中国人
} public static void main(String[] args) {
new Test().test();
}
}
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule { @Provides
Person providerNormalPerson() {
return new Person();
} @MyType(1)
@Provides
Person providerPerson() {
return new Person("普通人");
} @MyType(2)
@Provides
Person providerChinesePerson() {
return new Person("中国人");
} }
class Person {
public String name; public Person() {
name = "默认的名字";
} public Person(String name) {
this.name = name;
}
}
@Qualifier
@interface MyType {//自定义一个限定符
int value();
}

Component匹配多个Module,Named注解的使用

public class Test {
@Inject Person person;//会去Component指定的所有Module中查找提供Person的方法,但是会忽略Named等限定不一样的方法
@Named("2") @Inject Person person2; private void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println(person.name+" "+person2.name);
} public static void main(String[] args) {
new Test().test();//来自Module 来自Module2
}
}
@Component(modules = {MainModule.class, MainModule2.class})//Component匹配多个Module
interface MainComponent {
void inject(Test obj);
}
@Module
class MainModule {
@Provides
Person providerPerson() {
return new Person("来自Module");
}
} @Module
class MainModule2 {
@Named("2")//如果Component指定的多个Module中具有方法声明完全相同的两个方法,会编译失败。此时可以通过使用Named限定来解决。
@Provides
Person providerPerson() {//由于MainModule2中的此方法的声明和MainModule中的完全一样,所以不能放在同一个Module中
return new Person("来自Module2");
}
}
class Person {
public String name;
public Person(String name) {
this.name = name;
}
}

Module之间相互包含,演示include

@Component(modules = {MainModule.class})//依赖一个Module
interface MainComponent {
void inject(Test obj);
} @Module(includes = {APPModule.class})//依赖其他Module
class MainModule {
@Provides
Person providerPerson(String name) {//这里的name由其includes的APPModule提供
return new Person(name);
}
}
//**********************************************和下面的效果完全相同********************************************
@Component(modules = {MainModule.class, APPModule.class})//直接依赖多个Module
interface MainComponent {
void inject(Test obj);
} @Module
class MainModule {
@Provides
Person providerPerson(String name) {//这里的name由其includes的APPModule提供
return new Person(name);
}
}
@Module
class APPModule {
@Provides
String providerName() {
return "包青天";
}
}

依赖另一个Component,演示dependencies

经测试发现,使用继承关系,如【interface MainComponent  extends  AppComponent】除了原始的继承外(扩展了方法),没有任何额外的意义。也即MainComponent不会额外dependencies AppComponent,MainComponent的modules也不会额外添加AppComponent的modules。
public class Test {
@Inject Person person; private void test() {
AppComponent appComponent = DaggerAppComponent.builder().build();
DaggerMainComponent.builder()
.appComponent(appComponent)//关键步骤一,不添加运行时报错
.build().inject(this);
System.out.println(person.name);
} public static void main(String[] args) {
new Test().test();//包青天
}
}
@Component(dependencies = {AppComponent.class})//依赖另一个Component
interface MainComponent {
void inject(Test obj);
}
@Component(modules = {APPModule.class})
interface AppComponent {
Person getAPerson();//关键步骤二
//【被依赖的Component】必须提供【依赖方Component】需要的对象(因为依赖方缺少相应的Provides方法),如果不提供编译失败。方法名随意
}
@Module
class APPModule {
@Provides
Person providerPerson() {
return new Person("包青天");
}
}

Scope和Singleton:基于Component实例的单例模式

注意:以下演示效果中,使用"用Scope标注的"自定义注解MyScope,和使用Singleton注解的效果完全一致。事实上,Singleton和网上流传的PerActivity等之类的玩意的效果完全一样,其唯一的区别就是名字不一样。

注意:如果不使用Scope标注自定义注解MyScope,则所有返回的对象都是不同的对象。
结论:使用Scope或Singleton注解后,基于同一Component的实例可以具有单例效果;但是,要想保持为全局单例,就必须保证Component实例为全局单例。
public class Test {
public static void main(String[] args) {
new Test1().test();
new Test2().test();
}
}
class Test1 {
@Inject Person person, person1;
public void test() {
DaggerMainComponent.builder().mainModule(new MainModule()).build().inject(this);
System.out.println((person == person1) + " " + person.toString() + " " + person1.toString());
//true com.bqt.dagger.Person@29453f44 com.bqt.dagger.Person@29453f44
}
} class Test2 {
@Inject Person person, person1;
public void test() {
DaggerMainComponent2.builder().mainModule(new MainModule()).build().inject(this);
System.out.println((person == person1) + " " + person.toString() + " " + person1.toString());
//true com.bqt.dagger.Person@12a3a380 com.bqt.dagger.Person@12a3a380
}
}
@Module
class MainModule {
@MyScope//如果只在这里添加@Singleton(任何用Scope标注的注解)注解,编译失败!
@Provides
Person providerPerson() {
return new Person();
}
}
@MyScope//如果只在这里添加@Singleton注解任何用Scope标注的注解),没有任何效果。
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test1 obj);
} @MyScope//如果只在这里添加@Singleton注解任何用Scope标注的注解),没有任何效果。
@Component(modules = MainModule.class)
interface MainComponent2 {
void inject(Test2 obj);
} //************************************也可以使用同一个Component,效果和上面使用两个时完全一样**************************************
@MyScope
@Component(modules = MainModule.class)
interface MainComponent {
void inject(Test1 obj);
void inject(Test2 obj);
}
@Scope
@interface MyScope {
}
class Person {
public String name; public Person() {
name = "默认的名字";
}
}

全局单例模式:保证Component全局只有一个实例

基本步骤:
  1. 在Application中实例化AppComponent,保证全局AppComponent只有一个实例
  2. 通过AppComponent管理AppModule,使用@Singleton标注AppComponent以及AppModule中的方法
  3. 在AppComponent中定义需要注入全局类实例的方法
  4. 在AppModule中定义创建全局类实例的方法
  5. 在需要注入全局类实例的类中,通过全局的AppComponent实例将全局类实例注入到此类中
public class App extends Application {
public static App mApp;
private AppComponent mAppComponent; @Override
public void onCreate() {
super.onCreate();
mApp = this;
//1、在Application中实例化AppComponent,保证全局AppComponent只有一个实例
mAppComponent = DaggerAppComponent.builder().appModule(new AppModule()).build();
} public AppComponent getAppComponent() {
return mAppComponent;
}
}
@Singleton//2、使用@Singleton标注AppComponent
@Component(modules = AppModule.class)//2、通过AppComponent管理AppModule
interface AppComponent {
void injectPerson(GZ obj);//3、在AppComponent中定义需要注入全局类实例的方法
void injectPerson(SZ obj);
//void injectPerson(Object obj);//不能使用Object来代替GZ或SZ
} @Module
class AppModule {
@Provides
@Singleton//2、使用@Singleton标注AppModule中的方法
public Person providePerson() {
return new Person("包青天");//4、在AppModule中定义创建全局类实例的方法
}
} class Person {
public String name; @Singleton//这里的Singleton是没有意义的,但是加上去可以方便理解这个类的用途
public Person(String name) {
this.name = name;
}
}
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("bqt", "【是否为同一对象】" + (new GZ().person == new SZ().person));//true
}
}
class GZ {
@Inject public Person person;
public GZ() {
//5、在需要注入全局类实例的类中,通过全局的AppComponent实例将全局类实例注入到此类中
App.mApp.getAppComponent().injectPerson(this);
Log.i("bqt", "【GZ】" + person.toString());//【GZ】com.bqt.dagger.Person@e70c3b5
}
} class SZ {
@Inject public Person person;
public SZ() {
App.mApp.getAppComponent().injectPerson(this);
Log.i("bqt", "【SZ】" + person.toString());//【SZ】com.bqt.dagger.Person@e70c3b5
}
}

一个MVP架构下完整的Dagger2案例

PS:以下案例在项目中可以优化,比如一个界面应该用一个Component,比如如果不需要Model,Component可以不依赖任何Model。

在MVP架构中,最常见的依赖关系,就是Activity持有presenter的引用,并在Activity中实例化这个presenter,即Activity依赖presenter;而同时,presenter又需要依赖View接口,从而更新UI;同样,presenter和Model之间也需要相互依赖。这样一来,虽然,Activity和Model之间完全解耦了,但Activity与presenter、presenter和Model之间却紧紧耦合在了一起。

V:Activity接口及Activity

public interface IMainView {
void showToast(String src);
}
public class MainActivity extends AppCompatActivity implements IMainView {
@Inject IMainPresenter mainPresenter;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainPresenter
// 否则,必须声明为MainPresenter,因为此时框架是去查MainPresenter中使用@Inject标注的构造方法,而不是接口中的*** @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); DaggerMainComponent.builder()
.mainModule(new MainModule(this, "白乾涛"))
.build()
.inject(this);
mainPresenter.login("123");
} @Override
public void showToast(String src) {
Toast.makeText(this, src, Toast.LENGTH_SHORT).show();
}
}

P:Presenter接口及Presenter

public interface IMainPresenter {
void login(String password);
}
public class MainPresenter implements IMainPresenter {
private IMainView mainView;
private String name;
@Inject MainModel mainModel;//注意:如果是通过Module中@Provides注解标注的方法来生成对象,这里可以声明为IMainModel
// 否则,必须声明为MainModel,因为此时框架是去查MainModel中使用@Inject标注的构造方法,而不是接口中的*** public MainPresenter(IMainView mainView, String name) {
this.mainView = mainView;
this.name = name;
Log.i("bqt", "【构造MainPresenter】");
DaggerMainModelComponent.builder()
.mainModelModule(new MainModelModule())
.build()
.inject(this);
} @Override
public void login(String password) {
String info = mainModel.login(name, password);
if (mainView != null) mainView.showToast(info);
Log.i("bqt", info);
}
}

MainModule 和 MainComponent

@Module
public class MainModule {
private IMainView mainView;
private String name; public MainModule(IMainView mainView, String name) {
this.mainView = mainView;
this.name = name;
Log.i("bqt", "【构造MainModule】");
} @Provides
IMainPresenter provideMainPresenter() {
return new MainPresenter(mainView, name);
}
}
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);//这里必须指定要注入到哪个类里面,参数声明必须是MainActivity而不能是IMainView
}

M:Model相关的4个类

我把这些东西全部放在了MainPresenter类里面,不然文件膨胀太严重了!
//*******************************************以下是MVP中M相关的类***********************************************
interface IMainModel {//在这个案例中,抽象出的M接口完全没有存在的价值了
String login(String name, String password);
} class MainModel implements IMainModel {
@Override
public String login(String name, String password) {
return (password == null || password.equals("")) ? "请登录" : "登录成功,你的名字为:" + name;
} @Inject
public MainModel() {
Log.i("bqt", "【构造MainModel】");
}
} @Component(modules = MainModelModule.class)
interface MainModelComponent {
void inject(MainPresenter mainPresenter);
} @Module
class MainModelModule {
}
额,本来一个类可以搞定的事,现在一下子膨胀到10个类了 ^_^      O(∩_∩)O     \(^o^)/~
2017-9-18

【Dagger2】 案例大全的更多相关文章

  1. 2018 dnc 公司案例大全,迎接.NET Core开源新时代

    2018 dnc 公司案例大全,迎接.NET Core开源新时代   dnc = .NET Core.dotnet Core dnc是微软新一代主力编程平台,开源.免费.跨平台.轻量级.高性能,支持L ...

  2. Flume环境部署和配置详解及案例大全

    flume是一个分布式.可靠.和高可用的海量日志采集.聚合和传输的系统.支持在日志系统中定制各类数据发送方,用于收集数据;同时,Flume提供对数据进行简单处理,并写到各种数据接受方(比如文本.HDF ...

  3. 微信小程序案例大全

    微信小程序demo:足球,赛事分析 小程序简易导航 小程序demo:办公审批 小程序Demo:电魔方 小程序demo:借阅伴侣 微信小程序demo:投票 微信小程序demo:健康生活 小程序demo: ...

  4. js(=>) 箭头函数 详细解说 案例大全

    ES6标准新增了一种新的函数:Arrow Function(箭头函数). 为什么叫Arrow Function?因为它的定义用的就是一个箭头: x => x * x 上面的箭头函数相当于: fu ...

  5. 微信小程序源码案例大全

    微信小程序demo:足球,赛事分析 小程序简易导航 小程序demo:办公审批 小程序Demo:电魔方 小程序demo:借阅伴侣 微信小程序demo:投票 微信小程序demo:健康生活 小程序demo: ...

  6. iOS完全自学手册——[一]Ready?No!

    1.前言 今天开始我会不定期写一些iOS自学的相关文章.毕竟,自己是自学开始,知道自学有哪些坑,知道自学对于开发欠缺什么,此外,加上现在的实际开发经验,希望能给自学的iOS开发者一些建议. 2.Rea ...

  7. Flume NG 配置详解(转)

    原文链接:[转]Flume NG 配置详解 (说明,名词对应解释 源-Source,接收器-Sink,通道-Channel) 配置 设置代理 Flume代理配置存储在本地配置文件.这是一个文本文件格式 ...

  8. Ajax-ajax实例4-多级联动菜单

    项目结构: 项目运行: 技术要点: 1.4.1 技术要点在分析具体的实现代码之前,先介绍一下本例的几个技术要点.1 .选项的动态创建与删除document 对象的 createElement 方法可以 ...

  9. Ajax-ajax实例3-动态树形列表

    项目结构: 项目演示: 技术要点: 1.3.2 技术要点在基本原理的介绍中,了解到通过在父节点内动态创建子节点,并利用样式表缩进完成树形列表的基本框架.除了这一点外,还有下面一些问题需要考虑.1 .将 ...

随机推荐

  1. mysql高性能索引

    独立索引: 独立索引是指索引列不能是表达式的一部分,也不能是函数的参数 例1: SELECT actor_id FROM actor WHERE actor_id+1=5 --这种写法,就算在acto ...

  2. python opencv3 向图像里写字

    git:https://github.com/linyi0604/Computer-Vision # coding:utf-8 import cv2 img = cv2.imread(".. ...

  3. [BZOJ4864][BeiJing2017Wc]神秘物质(splay)

    首先merge就是先delete两次再insert,Max就是整个区间的最大值减最小值,Min就是区间中所有相邻两数差的最小值. Splay支持区间最大值,区间最小值,区间相邻差最小值即可. #inc ...

  4. 【并查集&&带权并查集】BZOJ3296&&POJ1182

    bzoj1529[POI2005]ska Piggy banks [题目大意] n头奶牛m种语言,每种奶牛分别掌握一些语言.问至少再让奶牛多学多少种语言,才能使得它们能够直接或间接交流? [思路] ( ...

  5. hdu 4442 Physical Examination 贪心排序

    Physical Examination Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...

  6. Hihocoder #1081 最短路径一 dijkstra

    #1081 : 最短路径·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 万圣节的早上,小Hi和小Ho在经历了一个小时的争论后,终于决定了如何度过这样有意义的一天—— ...

  7. Git_标签管理

    发布一个版本时,我们通常先在版本库中打一个标签,这样,就唯一确定了打标签时刻的版本.将来无论什么时候,取某个标签的版本,就是把那个打标签的时刻的历史版本取出来.所以,标签也是版本库的一个快照. Git ...

  8. 华为S5300系列升级固件S5300SI-V100R006C00SPC800.cc

    这个固件附带了web,V100R005可以直接升级到这个版本,但没什么必要,因为V100R005本身可以直接升级到V200. 升级小插曲: 1.升级的使用使用Windows,不要用Mac或者Linux ...

  9. select 语句的执行顺序

    select 语句的执行顺序 借用ItZik Ben-Gan.Lubor Kollar.Dejan Sarka所著的<Sql Server 2005 技术内幕:T-SQL查询>的一段话足以 ...

  10. .Net C#上传文件最大设置

    <!--网页允许的最大设置节点--> <system.web> <httpRuntime targetFramework="4.5" maxReque ...