Dagger2 是如何实现依赖注入的?

Dagger2流行已经有一段时间了,是一个很强大的依赖注入框架。以前接触过Spring的IOC,了解过它的实现原理是用反射实现的。而移动端是对资源很敏感的,Dagger2作为移动端的一个主流框架,肯定不会用一样的套路去玩。所以抽空研究了一下它的实现方式。

从一个最简单的例子入手

public class ActivityLogin extends Activity implements ILoginView {

        private static final String TAG = "ActivityLogin_";
        @Inject
        LoginPresenter loginPresenter;

        Button loginBtn;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main2);
            DaggerMyComponent.create().inject(this);
            loginPresenter.attachView(this);
            loginBtn = findViewById(R.id.btn_login);
            loginBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    loginPresenter.login();
                }
            });
        }

        @Override
        public void onLoginSuccess() {
            Log.i("MyModule", TAG + "LoginSuccess_");
            Toast.makeText(this, "ActivityLogin onLoginSuccess", Toast.LENGTH_LONG).show();
        }

        @Override
        public void onLoginError(String errorMsg) {

        }
    }

上面的例子是一个MVP的例子:要在一个Activity中注入一个LoginPresenter(在上面加了@Inject 注解)。来看LoginPresenter:

    public class LoginPresenter extends BasePresenter<ILoginView> {
        private static final String TAG = "LoginPresenter";
        @Inject
        public LoginPresenter() {

        }

        @Override
        public void onDetach() {
            Log.i("MyModule", TAG+" "+hashCode()+" detach_" + getActivityNameBelongTo());
        }

        @Override
        public void onAttach() {
            Log.i("MyModule", TAG+" "+hashCode()+" attach_" + getActivityNameBelongTo());
        }

        public void login() {
            Log.i("MyModule",TAG+" http request ...");
            getView().onLoginSuccess();
        }
    }

需要注入的类,LoginPresenter的构造函数上也加了@Inject注解。
然后看Component:

@Component()
    public interface MyComponent {
        void inject(ActivityLogin activityLogin);
    }

可以看到MyComponent是个接口,@Component注解是可以带参数的(Class

看看生成的文件

工程的目录结构:
build/generate/source/apt/debug/xxx(包名)下的结构:
可以看到,Dagger2帮我们生成了几个文件:

  1. 被注入依赖类,也就是ActivityLogin 对应一个ActivityLogin_MembersInjector类,以“XXX_MembersInjector”命名(XXX是被注入依赖类的类名),这个类会放在和ActivityLogin同一个包目录下。
  2. 依赖的类,也就是LoginPresenter ,对应一个LoginPresenter_Factory类,以“XXX_Factory”命名(XXX是依赖的类的类名,放在了LoginPresenter同一个包目录下。
  3. Component注解接口对应的一个类,以“DaggerXXX”命名(XXX师Component注解对应的接口类名),同样放在了对应的目录下。

我们使用的时候是DaggerXXXX.creat().inject(this),看下这个生成的DaggerXXXX:

public final class DaggerMyComponent implements MyComponent {
      private Provider<LoginPresenter> loginPresenterProvider;

      private MembersInjector<ActivityLogin> activityLoginMembersInjector;

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

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

      public static MyComponent create() {
        return builder().build();
      }

      @SuppressWarnings("unchecked")
      private void initialize(final Builder builder) {

        this.loginPresenterProvider =
            LoginPresenter_Factory.create(MembersInjectors.<LoginPresenter>noOp());

        this.activityLoginMembersInjector =
            ActivityLogin_MembersInjector.create(loginPresenterProvider);
      }

      @Override
      public void inject(ActivityLogin activityLogin) {
        activityLoginMembersInjector.injectMembers(activityLogin);
      }

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

        public MyComponent build() {
          return new DaggerMyComponent(this);
        }

        /**
         * @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
         */
        @Deprecated
        public Builder myModule(MyModule myModule) {
          Preconditions.checkNotNull(myModule);
          return this;
        }
      }
    }

这种情况是最简单的(依赖的类LoginPresenter没有其他依赖),所以这时候分析文件结构最清晰:

  1. DaggerMyComponent 实现了 MyComponent,也就说有了要Inject的宿主类,因为MyComponent这个接口;
  2. 有个用来装配Module的Builder,也就是说Module可以自己手动指定;
  3. 每个依赖的类如LoginPresenter都会有个Provider如Provider来包装它;每个被依赖的类如ActivityLogin都会有个MembersInjector来包装它;
  4. 如果依赖的类如LoginPresenter没有再继续有别的依赖,那么这个Provider由依赖的类的Factory生成;
  5. MembersInjector 由对应生成的ActivityLogin_MembersInjector来生成;
  6. 最后的inject其实是MembersInjector代理的。

贴出来LoginPresenter_Factory:

public final class LoginPresenter_Factory implements Factory<LoginPresenter> {
      private final MembersInjector<LoginPresenter> loginPresenterMembersInjector;

      public LoginPresenter_Factory(MembersInjector<LoginPresenter> loginPresenterMembersInjector) {
        assert loginPresenterMembersInjector != null;
        this.loginPresenterMembersInjector = loginPresenterMembersInjector;
      }

      @Override
      public LoginPresenter get() {
        return MembersInjectors.injectMembers(loginPresenterMembersInjector, new LoginPresenter());
      }

      public static Factory<LoginPresenter> create(
          MembersInjector<LoginPresenter> loginPresenterMembersInjector) {
        return new LoginPresenter_Factory(loginPresenterMembersInjector);
      }
    }

可以看到LoginPresenter_Factory 会有一个MembersInjector 成员,这个成员是构造函数依赖进来的;并且get方法里,有new的动作,也就是说,如果依赖的类是无参构造,最后依赖对象的实例化是在依赖对应的Factory中生成的

看ActivityLogin_MembersInjector:

public final class ActivityLogin_MembersInjector implements MembersInjector<ActivityLogin> {
      private final Provider<LoginPresenter> loginPresenterProvider;

      public ActivityLogin_MembersInjector(Provider<LoginPresenter> loginPresenterProvider) {
        assert loginPresenterProvider != null;
        this.loginPresenterProvider = loginPresenterProvider;
      }

      public static MembersInjector<ActivityLogin> create(
          Provider<LoginPresenter> loginPresenterProvider) {
        return new ActivityLogin_MembersInjector(loginPresenterProvider);
      }

      @Override
      public void injectMembers(ActivityLogin instance) {
        if (instance == null) {
          throw new NullPointerException("Cannot inject members into a null reference");
        }
        instance.loginPresenter = loginPresenterProvider.get();
      }

      public static void injectLoginPresenter(
          ActivityLogin instance, Provider<LoginPresenter> loginPresenterProvider) {
        instance.loginPresenter = loginPresenterProvider.get();
      }
    }

ActivityLogin_MembersInjector 会有一个Provider 的成员,这个成员是构造的参数,并且injectMembers方法持有被注入依赖类(ActivityLogin)的实例引用,instance.loginPresenter = loginPresenterProvider.get()真正做了“桥梁”,为ActivityLogin中的loginPresenter赋值。

梳理一下几个类的关系

简单的总结就是,DaggerMyComponent通过Factory获取了Provider包装的依赖的对象(Provider的实例)然后用它生成了ActivityLogin_MembersInjector的实例,最后这个实例包含了被依赖和依赖对象的引用,就可以完成搭桥了。

给LoginPresenter加依赖

 @Inject
    public LoginPresenter(Dependence1 dependence1) {

    }

如果这时候什么都不做,编译的时候会报错:
这是因为Dagger2并不知道Dependence1 的实例要从哪里来。对Dependence1做处理:

    public class Dependence1 {
        @Inject
        public Dependence1(){}
    }

给构造函数加Inject注解。
描述下变化:

  • 对应目录下多了一个Dependence1_Factory的类:
    public enum Dependence1_Factory implements Factory<Dependence1> {
          INSTANCE;

          @Override
          public Dependence1 get() {
            return new Dependence1();
          }

          public static Factory<Dependence1> create() {
            return INSTANCE;
          }
    }

这是个枚举单例,实现了Factory。可以看到,这里对Dependence1做了实例化(new 动作)

  • DaggerMyComponent中的initalize方法:
this.loginPresenterProvider =
            LoginPresenter_Factory.create(
                MembersInjectors.<LoginPresenter>noOp(), Dependence1_Factory.create());

对比之前,就是create方法多加了一个参数,这个参数由Dependence1_Factory提供。

其他都没有动。
对应的示意图:
所以基本可以看出:

  1. 生成的DaggerXXX类中会持有所有的注入依赖类对象的Provider和被注入对象的MemberInjector;
  2. 所有的提供依赖的类都会被包装成Provider,所有需要注入依赖的类都被包装成MemberInjector;
  3. 需要注入依赖的MemberInjector会通过create方法把所有依赖的Provider传进去,有几个依赖,create就有几个参数;
  4. 所有Provider的实例都是通过对应的Factory提供的。

小结

这只是一个最简单的使用Dagger2的例子,这样可以很清晰的搞清楚Dagger2为我们生成这么多文件它们之间的关系。后面会把其他的注解也分析,整体的分析思路是一样的。这一趟分析个人感觉有几个很nice的设计思路:

  • @Inject 修饰的成员不能是private的,因为生成的桥梁类MemberInjector和被注入类在一个包目录下面,private是不能访问的。放在含有注解的类所在包目录下面,可以最大程度保护成员的访问权限,不然只能是public;

  • DaggerXXX用了外观模式,拿到了本来只能在包目录下才能访问的依赖实例;

  • DaggerXXX实现了标注了@Component 的接口,这个接口指定了哪些类是需要注入依赖的类,所以我们在要注入依赖的类里使用DaggerXXX.create().inject(this)才这么方便。

使用的设计模式: 外观模式、 工厂模式、 单例模式

猜你喜欢

转载自blog.csdn.net/y1962475006/article/details/80014152