剖析Dagger2依赖注入

前言

本篇文章将结合我自己写的咖啡店卖咖啡的栗子的demo,结合代码来分析一下Dagger2是如何来实现依赖注入的。
在我的第一篇文章中有提到如何配置Dagger2以及Dagger2的几个关键概念,不清楚怎么配置的你,建议可以先看看我的第一篇文章,先配置好Dagger2再开始学习下面的内容,由于结合代码,大部分代码都贴出来的,篇幅较长,做好心理准备,前方预警。

总是想着可以开一家咖啡店,现在现实中开不了,那就现在代码里开一家吧。程序员不仅能宅出一个世界,更重要的你能创建出一个你自己的世界,是不是突然自己好厉害! 好吧,不扯了,回到现实中,好好学习吧。
我先创建一家销售咖啡的咖啡店,然后在里面买各种咖啡,黑咖啡呀等。既然是咖啡店,那肯定要有咖啡呀。所以咖啡就是我需要的一个对象。所以我的咖啡店还需要依赖于咖啡,没咖啡,那怎么行,准备好票子,来买我的咖啡吧。根据这个需求,我的咖啡店类如下:

public class CoffeeShopActivity extends AppCompatActivity {
  // 1. 我要卖的咖啡对象,用inject注明要依赖的对象
  @Inject
  Coffee mCoffee ; 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      inject();
      operate();
  }
  //2.添加依赖关键代码。
  private void inject() {
        DaggerCoffeeShopComponent.builder().build().inject(this);
 
  }

  private void operate() {
      mCoffee.getCoffee();
  }
}

Coffee是一个很简单的类,里面提供了一个getCoffee方法,打印了一串信息而已。

public class Coffee {
    private static final String TAG =     Coffee.class.getSimpleName();
    @Inject
    public Coffee(){

    }

    public void getCoffee() {
        Log.i(TAG, "black coffee");
    }
}

咖啡店里面是个很简单的依赖关系,CoffeeShopActivity依赖于类Coffee,可以看到我们没有用 new 来创建Coffee对象,而是直接通过@Inject来标明CoffeeShopActivity的mCoffee需要依赖注入,来实例化这个变量。是不是很神奇,如果你知道了依赖注入是怎么注入的之后,你就不会觉得很神奇了。下面我们一层层地来剥开这个“洋葱”吧。

首先在上面代码里,成员变量mCoffee定义的时候,添加了@Inject注释,然后Coffee的构造函数添加了@Inject注释。这样就CoffeeShopActivity告诉了Dagger2,我需要Coffee的实例,然后Dagger2就去找到Coffee的构造函数创建一个Coffee。难道就只要这两个类就可以了吗,之前不是说Component是目标类与被注入类之间的桥梁吗? Component呢? 没错,我还没贴出Componet呢。自定义个Componet类如下:

@Component
public interface CoffeeShopComponent  {
   void inject(CoffeeShopActivity coffeeShopActivity);
}

这个Component是不是很简单,只有一个抽象函数,方法名是inject,方法名不是必须是inject,也可以是其他名字,但是参数一定要是你的目标类,而且注意哦,不能是Activity,一定要是目标类自己的类名。在第一篇文章里面就有提到过,这里再强调一下,Dagger框架是通过参数的类型以及返回值类型,找到要依赖的类,从而建立联系的,所以类名一定要是直接要注入依赖的目标类,返回值也是要注入的类

好了,注射器(Component)写好了,那要派上用场呀!那是在哪里用注射器把依赖注入了MainActivity呢。请看,我在MainActivity里面第2个注释,inject()方法。没错就是这个方法,在这个方法里面用DaggerCoffeeShopComponent.builder().build().inject(this);来实现了注入。 等等,这个DaggerCoffeeShopComponet是哪里来的? 大家还记得在配置Dagger2框架环境的时候,引入了APT的包吗,这个包是一个gradle插件,用于结合注释自动生成代码,所以这个包一定要配置,否则不能自动根据Dagger2的注释来生成关键的代码。想要了解APT的可以看看这篇文章android-apt。 所以DaggerCoffeeShopComponent就是一个自动生成的实现了CoffeeShopComponent的类。文件路径是moudle路径\build\generated\source\apt\debug\com\gotech\changelauncherbg\demodagger\DaggerCoffeeShopComponent,下面自动生成的类都在这个路径里面。当然APT还根据注释自动生成了其他的代码。我们先分析这个,因为这个是实现依赖注入的入口类。注意啦,自动生成代码,是要你执行了rebuild project才会自动生成的,所以呢你编写完代码之后记得一定要rebuild才能得到这些类

好吧,下面就一起来看看DaggerCoffeeShopComponent是怎么把Coffee的实例,注入到mCoffee里面的。上代码:

//说明这段代码时生成的,并且生成自ComponetProcessor
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerCoffeeShopComponent implements CoffeeShopComponent {
private MembersInjector coffeeShopActivityMembersInjector;

private DaggerCoffeeShopComponent(Builder builder) {  
  assert builder != null;
  initialize(builder);
}

public static Builder builder() {  
  return new Builder();
}

public static CoffeeShopComponent create() {  
  return builder().build();
}
// 初始化
private void initialize(final Builder builder) {  
  this.coffeeShopActivityMembersInjector = CoffeeShopActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), Coffee_Factory.create());
}
//重写inject方法,实现依赖注入
@Override
public void inject(CoffeeShopActivity coffeeShopActivity) {  
  coffeeShopActivityMembersInjector.injectMembers(coffeeShopActivity);
}

public static final class Builder {
  private Builder() {  
  }

  public CoffeeShopComponent build() {  
    return new DaggerCoffeeShopComponent(this);
  }
}
}

从上面的代码可以知道,DaggerCoffeeComponent用有一个内部建造类Builder,可以看成一个简单的建造模式的应用。利用Builder来建造一个DaggerCoffeeShopComponet对象然后利用CoffeeShopActivity_MembersInjector初始化coffeeShopActivityMembersInjector。这个是真正实现依赖注入的类。从类名MembersInjector 可以看出这是一个用于注入成员变量的注入器,然后要注入的目标类是CoffeeShopActivity。 接下来,我们看看CoffeeShopActivity_MembersInjector这个类的代码。

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class CoffeeShopActivity_MembersInjector implements MembersInjector {
 private final MembersInjector supertypeInjector;
 private final Provider mCoffeeProvider;

 public CoffeeShopActivity_MembersInjector(MembersInjector supertypeInjector, Provider mCoffeeProvider) {  
   assert supertypeInjector != null;
   this.supertypeInjector = supertypeInjector;
   assert mCoffeeProvider != null;
   this.mCoffeeProvider = mCoffeeProvider;
 }

 @Override
 public void injectMembers(CoffeeShopActivity instance) {  
   if (instance == null) {
     throw new NullPointerException("Cannot inject members into a null reference");
   }
   supertypeInjector.injectMembers(instance);
   //1.获取一个Coffee的实例,复制给CoffeeShopActivity的实例instance,从这里可以看出mCoffee不能是私有的。
   instance.mCoffee = mCoffeeProvider.get();
 }

 public static MembersInjector create(MembersInjector supertypeInjector, Provider mCoffeeProvider) {  
     return new CoffeeShopActivity_MembersInjector(supertypeInjector, mCoffeeProvider);
 }
}

上面代码注释处,就是实现注入的地方,通过CoffeeProvider获取一个Coffee对象,然后复制给CoffeeShopActivity对象的mCoffee成员变量。
跟踪下来,终于知道是在什么地方给要依赖的成员变量赋值的了。其实我们可以看出,并不是没有用到new ,只不过Dagger2通过自动生成代码,帮你new了,你就省去了,这个new的过程,直接可以用了。这个呢,只是一个简单的成员变量的依赖跟踪,其他的通过module来提供依赖对象的例子,其实跟踪路径和这个大同小异。 我这里就以小见大的分析这么个小例子,更复杂的用法如果有什么疑问,欢迎留言交流。

总结

我的咖啡店开好了,咖啡都不用我自己去拿,自然有人给我送过来,真是方便至极。干了这杯咖啡。最后,我做个简单的关键点总结吧!

  • 被依赖的成员变量的修饰符,不能是私有的。否则不能实现依赖注入。
  • 每次编写完代码之后,记得rebuild project工程,自动生成代码。
  • 在使用成员变量之前,一定要先注入依赖,即编写一个类似于CoffeeShopActivityinject方法。注入之后才能使用。
  • Component的接口里面的方法,参数类型一定要是目标类,不能是目标类的父类。提供依赖的方法或者构造函数,返回的类型也一定要是 要被依赖的类的类名,不能是父类。

最后呢,虽然只是简单的一个成员变量的依赖注入的流程分析,但是其他复杂的依赖例子,其实看懂了这个,再分析其他的应该也挺简单的,关键是了解了原理。希望大家多多指点,哪里写的不对的,或者文章编写有什么建议的,都欢迎大家留言告知我。喜欢的在文末,轻轻点个赞哦。o 谢谢!Have a nice day!

你可能感兴趣的:(剖析Dagger2依赖注入)