只有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

最新文章

  1. JavaScript 常用代码
  2. 【Bootstrap】Bootstrap-select多选下拉框实现
  3. Upload Files To FTP in Oracle Forms D2k
  4. Mono for Android (4)-- 图片转为二进制,二进制转回图片
  5. spring与jpa整合 简化persistence.xml配置文件 使用属性文件 数据源dbcp访问数据库
  6. js中的prototye
  7. 用arm-linux-gcc v4.3.4交叉编译Qt4.8.3
  8. FreeMaker开发教程
  9. 自定义VS程序异常处理及调试Dump文件(一)
  10. 醒醒吧!互联网的真正未来不是AI,更不是VR,AR,而是区块链
  11. 51、css初识
  12. (转载)总结一下SQL语句中引号(')、quotedstr()、('')、format()在SQL语句中的用法
  13. 夜神模拟器链接Android studoid
  14. PHP使用CURL抓取网页
  15. shell 命令 创建/删除 软连接 ln -s
  16. Drools规则引擎入门指南(二)
  17. 基于centOS7:新手篇→tomcat的部署方式
  18. lua 设置文件运行的环境
  19. Python下Tesseract Ocr引擎及安装介绍
  20. jquery中的attr与prop的区别,什么时候用attr,什么时候用prop

热门文章

  1. Springboot listener
  2. Kubernetes(k8s)集群部署(k8s企业级Docker容器集群管理)系列之flanneld网络介绍及部署(三)
  3. ActiveMQ (三):项目实践
  4. MySQL 类型转换
  5. NetCore+Dapper WebApi架构搭建(六):添加JWT认证
  6. 机器学习之路:python 文本特征提取 CountVectorizer, TfidfVectorizer
  7. 在python中独立运行orm
  8. [Hdu4372] Count the Buildings
  9. GIT(1)----更新代码和上传代码操作的步骤
  10. Mac下使用ABTestingGateway快速搭建灰度网关