Image
Android/Dependency Injection

[Dagger2 심화] 1. @Provides의 특수한 형태 @Binds

@Binds는 @Provides의 특수한 형태일 뿐이며, @Provides와 같은 역할을 한다. 다른 점은 Binds에는 여러 제약이 있고, 제약이 많은 만큼 코드를 덜 생성한다는 점이다. 지금부터 @Binds에 어떠한 제약이 있는지 어떤 경우에 사용이 되어야 하는지 살펴보자.

 

@Binds

@Binds는 하나의 객체를 변수로 받아 해당 변수를 생성(생성자 주입)하고 프로퍼티, 메서드까지 @Inject가 붙은 모든 주입을 처리하고 나서 반환하는 Annotation이다. @Binds는 @Provides와 같은 역할을 하지만, @Provides에 비해 많은 제약이 있어 코드를 덜 생성한다.

 

@Binds를 쓰기 위한 조건으로는 두 개의 필수적인 조건이 있다.

  • @Binds는 추상(abstract) 클래스의 추상(abstract) 메서드에 붙이는 것만 유효하다.
  • @Binds가 붙은 메서드는 반드시 하나의 매개 변수만을 가져야 한다.

또한 다음과 같은 특징이 있다.

  • 추상 클래스에서 구현되어야 하므로, 추상 클래스에 Provider을 넣으려면 static 한 companion object에서만 넣을 수 있다.
  • 인자로 받는 값을 Provide로 제공받지 않아도 생성자 주입을 통해 생성 된다.
  • 인자로 받는 값의 타입이 아닌 인자로 받는 값이 구현하는 다른 타입으로 return 타입을 만들어야 한다. 같은 타이븡로 반환되면 주입이 무한 반복되는 cycle이 생기기 때문이다. (리턴 값이 다시 인자로 주입)

즉, @Binds는 다음과 같이 작성 가능하다.

@Module
abstract class PersonModule {
    @Binds
    abstract fun providesStudentWithName(student: Student) : Person

    companion object{
        @Provides
        fun providesString() = "StudentA"
    }
}

 

@Binds 예제

예제5에서 @Binds가 어떻게 처리되는지 자세히 다루어보자.

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

 

예제5의 구조는 다음과 같다.

그림1. @Binds 예제

*컴포넌트

@Component(modules = [PersonModule::class])
interface SchoolComponent{
    fun getStudent() : Student
}

*모듈

@Module
abstract class PersonModule {
    @Binds
    abstract fun providesStudentWithName(student: Student) : Person

    companion object{
        @Provides
        fun providesString() = "StudentA"
    }
}

*Person Interface

interface Person {
    fun getPersonName() : String
}

*Student Class

class Student @Inject constructor() : Person {
    @Inject
    lateinit var name: String

    override fun getPersonName() = name
}

이제 이 클래스들을 바탕으로 코드를 RunExample.kt에서 실행한다.

fun main(){
    val personComponent : SchoolComponent = DaggerSchoolComponent.create()
    val student : Person = personComponent.getStudent()
    println(student.getPersonName()) // StudentA
}

위 실행코드는 <그림2>의 과정으로 처리된다.

 

그림2. Bind과정

  1. Binding을 시작한다.
  2. Binds의 Student 인자는 Student Class의 생성자 주입에 의해 생성된다.
  3. Student의 내부 프로퍼티인 name에 Inject하기 위해 Provider에서 String을 생성한다.
  4. Provider에서 생성된 String을 Student에 Inject한 후 Student는 Person으로 형변환된다.
  5. Person에 Injection된다. 

 

즉, 실제 실행 과정은 Provider와 다른게 없다. 다른 점은 여러 제약이 걸림으로써 처리해야할 부분이 많이 줄어들어 generation된 코드가 줄어든다는 점이다.

반응형

 

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

 

 

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

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

open.kakao.com