Image
Android/Dependency Injection

[Dagger2] 7. Component Builder와 @BindsInstance

예제6

예제 링크 : https://github.com/seyoungcho2/Dagger2Example


이 글은 예제 6과 함께 한다. 예제 6의 코드 구조는 다음과 같다.

그림6. Component Builder&BindsInstance

 

Component와 Component Builder

이전 예제에서 generate된 코드를 보면 다음과 같은 코드가 생성되어 있다.

public final class DaggerSchoolComponent implements SchoolComponent {
  ..

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

    public SchoolComponent build() {
      return new DaggerSchoolComponent();
    }
  }
}

 

별도로 Builder을 설정하지 않아도 Component내부에 Builder가 만들어져 있다.

이러한 Builder을 설정하기 위해서는 Component Builder을 만들어야 한다. Builder을 따로 설정하지 않으면 위와 같은 Builder가 자동 생성된다.

 

Builder을 설정하는 방법을 살펴보기 위해 예제6의 AppComponent Interface를 보자. 

@Component(modules = [AppModule::class])
interface AppComponent {

    fun getApplication() : Application

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun name(name: String) : Builder
        fun build(): AppComponent
    }
}

AppComponent내부에 @Component.Builder라는 Builder 인터페이스를 만들어 놓았다. 이렇게 만들면 컴파일 타임에 다음과 같은 DaggerAppComponent class 파일이 생성된다.

public final class DaggerAppComponent implements AppComponent {
  ..
  private static final class Builder implements AppComponent.Builder {
    private String name;

    @Override
    public Builder name(String name) {
      this.name = Preconditions.checkNotNull(name);
      return this;
    }

    @Override
    public AppComponent build() {
      Preconditions.checkBuilderRequirement(name, String.class);
      return new DaggerAppComponent(name);
    }
  }
}

 

기존 빌더와 달라진점은 빌더 내부에 name메서드가 들어가고 해당 메서드를 통해 값을 name을 세팅해준다. 이제부터 AppComponent 내부의 모든 모듈들은 name 값 즉, String 값을 별도로 제공(Provide) 하지 않아도 사용할 수 있게 된다.

 

@BindsInstance

이것이 가능한 이유는 바로 @BindInstance 어노테이션 때문이다. 특정 인스턴스를 제공(Provide)하는 @BindInstance가 Component의 빌더안에 설정되면 해당 Component는 BindInstance에 의해 제공된 Component 및 그 하위의 모든 모듈에 제공(Provide)할 수 있게 된다.

 

따라서 AppModule이 아래와 같이 설정되어 name : String을 제공(Provide)하는 Provider가 존재하지 않더라도 위의 Component에서 Provide한 name 값을 가져다 쓸 수 있게 된다. 

@Module
class AppModule {
    @Provides
    fun providesApplication(name : String) = Application(name)
}

 

이를 오브젝트 그래프로 나타내면 다음과 같다.

그림2. BindsInstance 

 

위의 컴포넌트와 모듈로 다음의 코드를 돌리면

fun main() {
    val appComponent: AppComponent = DaggerAppComponent.builder().name("aa").build()
    println(appComponent.getApplication().name) // aa
}

 

aa라는 결과값이 나온다. builder에서 이름을 aa로 설정했기 때문이다.

 

정리

@BindInstance는 Provider의 특수한 형태이다. @Bind 와 관련된 Annotation은 모두 Provider이며, 모두 특수한 형태로 Provide하는 방식에 지나지 않는다. 

 

 

 

반응형

 

이 글의 저작권은 '조세영의 Kotlin World' 에 있습니다. 글, 이미지 무단 재배포 및 변경을 금지합니다.

 

 

Kotlin, Android, Spring 사용자 오픈 카톡

오셔서 궁금한 점을 질문해보세요!
비밀번호 : kotlin22

open.kakao.com