[Android/DI] Dagger2 - Qualifier 어노테이션

2021. 6. 27. 19:40·Android/Dependency Injection
반응형

이번 포스트에서 다룰 내용은 Qualifier 어노테이션입니다. 이름 그대로 객체를 식별해주는 어노테이션입니다.

@Named Qualifer

Dagger는 의존성 주입을 요청했을 때 Module에 선언된 자료형(Type)과 동일한 객체를 주입해줍니다.

따라서, Module에 동일한 Type의 객체가 여러개 선언되어 있을 경우, 컴파일 타임에 에러를 발생하게 됩니다.

 

의존성주입은 Interface를 기반으로하는 코드를 권장하므로, 같은 부모를 가진 여러 Child 클래스가 존재할 가능성이 매우 높습니다.

 

이러한 경우를 위해서 @Named 어노테이션을 통해서 같은 타입의 여러 객체를 선언하고 주입해줄 수 있습니다.

 

사용법은 간단합니다. Module의 @Provides어노테이션과 @Injection 어노테이션에 각각 @Named 어노테이션을 통해 어떠한 객체를 주입할 건지 선언해 주면 Module에 @Named로 선언된 객체와 동일한 객체를 주입해줄 수 있습니다.

ComputerComponent.kt

@Component(modules = [ComputerModule::class])
interface ComputerComponent {

    fun getComputerB(): ComputerB // Provision Method

    fun inject(computerA: ComputerA) // Member - Injection
    
    @Component.Builder
    interface Builder {

        fun computerModule(computerModule: ComputerModule): Builder

        fun application(@BindsInstance context: Context): Builder

        fun build(): ComputerComponent
    }
}

ComputerModule.kt

@Module
class ComputerModule {

    @Named("M1")
    @Provides
    fun provideM1Cpu(): Cpu {
        println("create Cpu")
        return M1Cpu()
    }

    @Named("I7")
    @Provides
    fun provideI7Cpu(): Cpu {
        println("create Cpu")
        return I7Cpu()
    }

    @Named("Ram4G")
    @Provides
    fun provideRam4G(): Ram {
        println("create Ram")
        return Ram4G()
    }

    @Named("Ram8G")
    @Provides
    fun provideRam8G(): Ram {
        println("create Ram")
        return Ram8G()
    }
}

Cpu.kt

interface Cpu {
    val name: String
    val company: String
}

class I7Cpu : Cpu {

    override val name: String
        get() = Name

    override val company: String
        get() = "Intel"

    companion object {
        const val Name = "I7 CPU"
    }
}

class M1Cpu : Cpu {

    override val name: String
        get() = "M1 Chip Cpu"

    override val company: String
        get() = "Apple"

    companion object {
        const val Name = "M1 Chip Cpu"
    }
}

Ram .kt

interface Ram {
    val name: String
    val company: String
}

class Ram4G : Ram {

    override val name: String
        get() = Name

    override val company: String
        get() = "Samsung"

    companion object {
        const val Name ="4G RAM"
    }
}

class Ram8G : Ram {

    override val name: String
        get() = Name

    override val company: String
        get() = "Hynix"

    companion object {
        const val Name ="8G RAM"
    }
}
class ComputerA {

    @Named("M1")
    @Inject
    lateinit var cpu: Cpu

    @Named("Ram4G")
    @Inject
    lateinit var ram: Ram
}
class ComputerB @Inject constructor(@Named("I7") val cpu: Cpu, @Named("Ram8G") val ram: Ram)

Test Code

@Config(manifest = Config.NONE, sdk = [28])
@RunWith(RobolectricTestRunner::class)
class ComputerUnitTest {

    private lateinit var computerComponent: ComputerComponent
    private lateinit var context: Context

    @Before
    fun init() {
        context = InstrumentationRegistry.getInstrumentation().targetContext
        computerComponent = DaggerComputerComponent.builder().application(context).build() // Component Builder
    }

    @Test
    fun testComputerMemberInjection() {
        val computerA =
            ComputerA()

        computerComponent.inject(computerA) // Field Inject

        Assert.assertEquals(M1Cpu.Name, computerA.cpu.name)
        println("computerA's cpu = ${computerA.cpu.name}")
        println("computerA's ram = ${computerA.ram.name}")
    }

    @Test
    fun testComputerConstructorInject() {
        val computerB = computerComponent.getComputerB() // Constructor Inject
        Assert.assertEquals(I7Cpu.Name, computerB.cpu.name)
        println("computerB's cpu = ${computerB.cpu.name}")
        println("computerB's ram = ${computerB.ram.name}")
    }
}

Test 결과

computerA Test
computerB Test

사용자 정의 Qualifer

사용자 정의 식별자는 @Named 어노테이션에 String을 넣어주었던 것과는 다르게 직접 어노테이션을 생성해서 주입해줄 수 있습니다.

사용법은 @Named와 동일하게 Module의 @Provides와 @Inject에 각각에 정의했던 어노테이션을 선언해주면 해당하는 객체를 주입받을 수 있습니다.

 

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GetM1Cpu

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GetI7Cpu

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GetRam4G

@Qualifier
@MustBeDocumented
@Retention(AnnotationRetention.RUNTIME)
annotation class GetRam8G

 

@Module
class ComputerModule {

    @GetM1Cpu
    @Provides
    fun provideM1Cpu(): Cpu {
        println("create Cpu")
        return M1Cpu()
    }

    @GetI7Cpu
    @Provides
    fun provideI7Cpu(): Cpu {
        println("create Cpu")
        return I7Cpu()
    }

    @GetRam4G
    @Provides
    fun provideRam4G(): Ram {
        println("create Ram")
        return Ram4G()
    }

    @GetRam8G
    @Provides
    fun provideRam8G(): Ram {
        println("create Ram")
        return Ram8G()
    }
}
class ComputerA {

    @GetM1Cpu
    @Inject
    lateinit var cpu: Cpu

    @GetRam4G
    @Inject
    lateinit var ram: Ram
}
class ComputerB @Inject constructor(@GetI7Cpu val cpu: Cpu, @GetRam8G val ram: Ram)
@Config(manifest = Config.NONE, sdk = [28])
@RunWith(RobolectricTestRunner::class)
class ComputerUnitTest {

    private lateinit var computerComponent: ComputerComponent
    private lateinit var context: Context

    @Before
    fun init() {
        context = InstrumentationRegistry.getInstrumentation().targetContext
        computerComponent = DaggerComputerComponent.builder().application(context).build() // Component Builder
    }

    @Test
    fun testComputerMemberInjection() {
        val computerA =
            ComputerA()

        computerComponent.inject(computerA) // Field Inject

        Assert.assertEquals(M1Cpu.Name, computerA.cpu.name)
        println("computerA's cpu = ${computerA.cpu.name}")
        println("computerA's ram = ${computerA.ram.name}")
    }

    @Test
    fun testComputerConstructorInject() {
        val computerB = computerComponent.getComputerB() // Constructor Inject

        Assert.assertEquals(M1Cpu.Name, computerB.cpu.name)
        println("computerB's cpu = ${computerB.cpu.name}")
        println("computerB's ram = ${computerB.ram.name}")
    }
}

computerA Test
computerB Test

 

반응형
'Android/Dependency Injection' 카테고리의 다른 글
  • [Android/DI] Dagger2 - Scope와 dagger.android
  • [Android/DI] Dagger2 - MultiBinding 주입하기
  • [Android/DI] Dagger2 - Lazy Injection vs Provider Injection
  • [Android/DI] Dagger2 - 소개 및 Module과 Component
seunghwaan
seunghwaan
공부한 내용을 정리하는 개발 기록 블로그
    반응형
  • seunghwaan
    SH's Devlog
    seunghwaan
  • 전체
    오늘
    어제
    • 분류 전체보기 (148)
      • Android (62)
        • Basic (17)
        • Kotlin(Java) (14)
        • UI & Animation (1)
        • Compose (2)
        • Coroutines (1)
        • Dependency Injection (6)
        • RxJava (8)
        • BLE (3)
        • TDD (2)
        • JetPack (1)
        • NextStep (4)
        • Error Log (3)
      • Flutter (14)
        • Basic (5)
        • Dart (1)
        • State Management (2)
        • Widgets (4)
        • Error and Tips (2)
      • iOS (8)
        • Basic (0)
        • Swift (8)
      • Web Frontend (4)
        • JavaScript(TS) (4)
        • React (0)
      • CS(Computer Science) (18)
        • Network (4)
        • Database (10)
        • Design Pattern (1)
        • Computer Architecture (3)
        • Operating System (0)
      • Cloud (6)
        • AWS (6)
      • DevOps (25)
        • GIT (4)
        • CI CD (8)
        • Linux (4)
        • Docker (9)
        • Error Log (0)
      • 코딩테스트 (10)
        • DB (6)
        • 알고리즘 (4)
      • Backend (1)
        • Spring (1)
      • Mac Tip (0)
      • Language (0)
        • English (0)
        • Japanese (0)
      • Temporary (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Swift
    Jenkins
    database
    IOS
    CICD
    Algorithm
    Computer Science
    AWS
    BLE
    error
    Dependency Injection
    CI
    Kotlin
    시작하세요! 도커
    MySQL
    di
    컴퓨터공학
    docker
    Linux
    cognito
    네트워크
    cs
    Network
    상태 관리
    JavaScript
    Android
    RxJava
    FLUTTER
    gradle
    Dagger
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
seunghwaan
[Android/DI] Dagger2 - Qualifier 어노테이션
상단으로

티스토리툴바