brunch

You can make anything
by writing

C.S.Lewis

by 이승현 May 15. 2017

Dagger 2 #02

Dagger 2

Dagger 2


Dagger is a fully static, compile-time dependency injection framework for both Java and Android.


Dagger는 자바와 안드로이드를 위한 의존성 주입 프레임워크입니다.

Dagger를 이용해 의존성 주입(Dependency Injection)을 어떻게 구현하는지 간단하게 살펴보겠습니다.




#01 Declaring Dependencies


아래 코드는 이전 글에서 살펴본 의존성 주입 코드입니다.

단순히 Video 인스턴스 변수(의존성)를 MediaController 생성자를 통해 주입하는 코드입니다.

자바 코드만으로 의존성 주입을 쉽게 구현할 수 있지만, 만약 MediaController 클래스의 의존성이 변한다면, new MediaController 코드도 매번 수정해야 하는 문제가 있습니다.

public class MediaController {

    Video video;

    MediaController() { // Dependency Non-Injection
        video = new Video();
    }

    MediaController(Video video) { // Dependency Injection
        this.video = video; 
    }

    public String getCurrentPlayTime() {
        int currentPosition = video.getCurrentPosition();
        return getPlayTime(currentPosition);
    }

}




javax.inject.Inject annotation을 이용하면 Dagger에 의존성을 요청하기 때문에, 명시적으로 의존성을 주입시킬 필요가 없습니다.

따라서 MediaController 클래스의 의존성이 변해도 코드를 수정할 필요가 없습니다.


#01 Inject Annotation


@Inject annotation을 통해 의존성을 생성자에 선언(Declaring)하면, 아래와 같이 Dagger를 통해 의존성을 주입하게 됩니다.

1. @Inject annotation 선언
2. new MediaController 인스턴스 요청
3. Dagger가 필수 parameter(Video video)를 가져와 MediaController 생성자 호출
import javax.inject.Inject;

public class MediaController {

    Video video;

    @Inject
    MediaController(Video video) { // Dependency Injection with Inject annotation
        this.video = video;
    }

    public String getCurrentTime() {
        int currentPosition = video.getCurrentPosition();
        return Integer.toString(currentPosition);
    }

}


생성자 외에도 메서드와 필드에도 @Inject annotation을 선언할 수 있습니다.

자세한 내용은 아래 링크를 참조해 주시기 바랍니다.

http://docs.oracle.com/javaee/7/api/javax/inject/Inject.html




#02 Satisfying Dependencies


Dagger는 위에 언급한 대로, @Inject annotation 클래스의 인스턴스를 구성하여 각 의존성을 충족시킵니다. 이를 위해 아래 두 가지 annotation이 이용해야 합니다.


dagger.Provides annotation

어떻게 의존성을 구성하고 제공하는지 정의하는 메서드에 이용합니다.


dagger.Module annotation

의존성을 제공하는 @Provides 메서드를 가진 클래스에 이용합니다.
모든 @Provides 메서드는 @Module 클래스 안에 속해야 합니다.


#02 Provides, Module Annotation


import dagger.Module;
import dagger.Provides;

// By convention, @Provides methods are named with a provide prefix and module
// classes are named with a Module suffix.

@Module
public class MediaModule {

    @Provides
    public Video provideVideo() {
        return new Video();
    }

}




#03 Building the Graph



@Inject와 @Module 사이의 의존성을 연결하기 위해서는 dagger.Component annotation을 이용해야 합니다.

@Component는 Interface에만 이용할 수 있으며, Component를 구성하는 모든 @Module 클래스 목록을 작성해야 합니다.


#03 Component Annotation


import dagger.Component;

@Component(modules = {MediaModule.class})
public interface MediaComponent {

    MediaController mediaController();

}




@Component interface까지 만들면, Dagger는 이 interface를 implement 한 클래스를 자동으로 생성합니다. 생성된 클래스는 @Component interface 이름 앞에 "Dagger"를 붙이게 됩니다.

예를 들면, @Component interface MediaComponent {...}는 DaggerMediaComponent라는 이름을 가진 클래스를 생성합니다.


#04 Generated Dependency-injected implementation


import dagger.internal.Preconditions;
import javax.annotation.Generated;
import javax.inject.Provider;

@Generated(
  value = "dagger.internal.codegen.ComponentProcessor",
  comments = "https://google.github.io/dagger"
)
public final class DaggerMediaComponent implements MediaComponent {
  private Provider<Video> provideVideoProvider;

  private Provider<MediaController> mediaControllerProvider;

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

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

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

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

    this.provideVideoProvider = VideoModule_ProvideVideoFactory.create(builder.videoModule);

    this.mediaControllerProvider = MediaController_Factory.create(provideVideoProvider);
  }

  @Override
  public MediaController mediaController() {
    return new MediaController(provideVideoProvider.get());
  }

  public static final class Builder {
    private VideoModule videoModule;

    private Builder() {}

    public MediaComponent build() {
      if (videoModule == null) {
        this.videoModule = new VideoModule();
      }
      return new DaggerMediaComponent(this);
    }

    public Builder videoModule(VideoModule videoModule) {
      this.videoModule = Preconditions.checkNotNull(videoModule);
      return this;
    }
  }
}




이제까지 했던 짓거리? 들은 궁극적으로 Dagger를 이용해 의존성 주입을 구현하고, MediaController instance를 얻어오기 위함이었습니다.


이를 위해 build()와 create() 두 가지 방법을 이용할 수 있습니다.


1. Dagger에 의해 생성된 DaggerMediaComponent 클래스의 builder() 메서드 호출
2. @Component를 구성하는 Module 인스턴스(new VideoModule()) 설정
3. build() 메서드 호출
4. MediaController 인스턴스 가져오기
// build
MediaComponent component = DaggerMediaComponent.builder()
                .videoModule(new VideoModule())
                .build();
MediaController controller= component.mediaController();


다른 Component와의 의존성이 없고, arg가 없는 default 생성자로 Module을 생성할 수 있으면 create() 메서드를 이용할 수 있습니다.

1. Dagger에 의해 생성된 DaggerMediaComponent 클래스의 create() 메서드 호출
2. MediaController 인스턴스 가져오기
// create
MediaComponent component = DaggerMediaComponent.create();
MediaController controller= component.mediaController();


#05 Create Component


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // create
        MediaComponent component = DaggerMediaComponent.create();
        MediaController controller= component.mediaController();
        String currentTime = controller.getCurrentTime();

        // build
        component = DaggerMediaComponent.builder()
                        .videoModule(new VideoModule())
                        .build();
        controller= component.mediaController();
        currentTime = controller.getCurrentTime();

        Log.d("Dagger", currentTime);
    }
}




#04 build.gradle


dependencies {
    ...
    compile 'com.google.dagger:dagger:2.11-rc'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11-rc'
    provided 'javax.annotation:jsr250-api:1.0'
}




https://google.github.io/dagger/users-guide.html






Dagger 2 어렵습니다ㅠ

일단 이해한 부분만 작성했는데, 활용법은 이 외에도 무궁무진하네요.

저도 공부하면서 작성한거라, 혹시 틀린 부분이 있으면 댓글 달아주세요.

5월동안은 남은 몇가지 Dagger annotation과 안드로이드에서 좀 더 활용할 수 있는 방법에 대해 공부하고 글을 쓰려 합니다.

브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari