dagger

Dagger2 Singleton

android by kotlin 2022. 12. 3. 22:10

① Member-Inject Method 또는 Provision Method 를 호출하면 Dagger 에 의해서 주입되거나 반환될 인스턴스가 매번 새로 생성됩니다. 만약 인스턴스를 매번 생성하지 않고 한번 생성된 인스턴스를 특정 스코프 내에서 계속해서 사용해야 하는 경우에는 @Singleton 어노테이션을 사용해야 합니다.

 

② @Provides 메서드에 의해 제공되는 인스턴스를 싱글톤 패턴으로 만들기 위해서는 @Provides 어노테이션에 추가로 @Singleton 어노테이션을 붙인 후 해당 모듈이 연결된 Component 에도 @Singleton 어노테이션을 붙여야 합니다.

 

③ 하나의 ComponentImpl 인스턴스를 통해 싱글톤 인스턴스를 요청할 때는 항상 동일한 인스턴스를 반환해주지만, 같은 ComponentImpl 클래스이지만 다른 인스턴스인 경우에는 동일한 인스턴스를 반환하지 않습니다. 즉 동일한 Component 인스턴스 내에서만 동일한 싱글톤 인스턴스가 반환됩니다.

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

		val component1 = DaggerAppComponent.create()
        val component2 = DaggerAppComponent.create(0
        
//instance1 과 instance2 는 동일한 인스턴스이지만
//instance3은 instance1, instance2 와는 다른 새로운 인스턴스입니다.
        val instance1 = component1.getInstance()
        val instance2 = component2.getInstance()
   
        val instance3 = component3.getInstance()
    }

 

④ Dagger 코드 분석

 

ComponentImpl 클래스

  private static final class SingletonComponentImpl implements SingletonComponent {
    private final SingletonComponentImpl singletonComponentImpl = this;

    private Provider<Instance> provideInstanceProvider;

    private SingletonComponentImpl(SingletonModule singletonModuleParam) {

      initialize(singletonModuleParam);

    }

    /*
    *   ComponentImpl 의 필드에 DoubleCheck(Provider 구현 클래스) 인스턴스의 참조를 저장합니다.
    *   DoubleCheck 인스턴스의 필드에는 Component 의 요청에 의해 @Provides 메서드를 호출하는 Factory 클래스의 인스턴스의 참조를 저장하고 있습니다.
    * */
    @SuppressWarnings("unchecked")
    private void initialize(final SingletonModule singletonModuleParam) {
      this.provideInstanceProvider = DoubleCheck.provider(SingletonModule_ProvideInstanceFactory.create(singletonModuleParam));
    }

    @Override
    public void inject(MainActivity mainActivity) {
      injectMainActivity(mainActivity);
    }

    /*
    *   initialize 메서드에서 저장한 DoubleCheck 인스턴스의 참조를 통해 필드 주입에 필요한 인스턴스를 가져온 후 필드 주입을 합니다.
    * */
    private MainActivity injectMainActivity(MainActivity instance) {
      MainActivity_MembersInjector.injectInstance(instance, provideInstanceProvider.get());
      return instance;
    }
  }

 

 

DoubleCheck.provider 메서드

  public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
    checkNotNull(delegate);
    /*
    	delegate 인스턴스가 DoubleCheck 타입인지 확인합니다.
        매개변수로 전달된 delegate 인스턴스는 Provider 인터페이스를 구현한 Factory 클래스입니다.
    */
    if (delegate instanceof DoubleCheck) {
      /* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
       * binding, we shouldn't cache the value again. */
      return delegate;
    }
    /*
    	delegate 인스턴스가 DoubleCheck 타입이 아닌 경우에는 
        DoubleCheck 타입으로 래핑합니다.
    */
    return new DoubleCheck<T>(delegate);
  }

 

 

"모듈명_메서드명Factory" 클래스

public final class SingletonModule_ProvideInstanceFactory implements Factory<Instance> {
  private final SingletonModule module;

  public SingletonModule_ProvideInstanceFactory(SingletonModule module) {
    this.module = module;
  }

  @Override
  public Instance get() {
    return provideInstance(module);
  }

/*
	DoubleCheck.provider 메서드의 매개변수로 전달되는 인스턴스를 생성하는 static 메서드입니다.
    내부에 Module 을 가지고 있습니다.
*/
  public static SingletonModule_ProvideInstanceFactory create(SingletonModule module) {
    return new SingletonModule_ProvideInstanceFactory(module);
  }

  public static Instance provideInstance(SingletonModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.provideInstance());
  }
}

 

 

DoubleCheck get 메서드

  /*
  	외부 클래스(예: MainActivity)에 의해 의존성 주입 요청이 들어오면 
    주입할 인스턴스를 받아오기 위해 ComponentImpl 의 필드에 저장되어 있는 
    Provider 타입의 인스턴스의 get 메서드를 호출합니다.
    이 메서드는 동일한 인스턴스를 여러번 요청하더라도 동일한 인스턴스를 반환하도록 구현되어 있습니다.
    kotlin 의 by lazy 를 사용했을 때 생성되는 코드의 형태와 유사합니다.
  */
  @Override
  public T get() {
    Object result = instance;
    if (result == UNINITIALIZED) {
      synchronized (this) {
        result = instance;
        if (result == UNINITIALIZED) {
          result = provider.get();
          instance = reentrantCheck(instance, result);
          /* Null out the reference to the provider. We are never going to need it again, so we
           * can make it eligible for GC. */
          provider = null;
        }
      }
    }
    return (T) result;
  }

 

⑤요약 → Dagger 에 의해 자동으로 주입되는 인스턴스를 싱글톤 패턴으로 생성하기 위해서는 @Singleton 어노테이션을 사용하면 됩니다.