#android開発者向けじゃないdagger2入門メモ。(続くかな?)
・動機1
ググってもandroidがからむネタばかりなのと、そもそもspringユーザーが多いから圧倒的に情報の総量も少ないことと、年明けたのでちょっとずつ書いていかなきゃというメモです。
・動機2
起動時に時間かかるの好きじゃないんです。で、起動し終わったら何かしらのミスでinjectionミスでエラーとか出ると切なくなります。CI回すから大丈夫とかだと遅いなぁと。そういうのを極小化してくれそうなところをdagger2に期待しています。コンパイル時にって素敵じゃないですか。Javaですからね。(慣れておけばandroidアプリ開発が回ってきても一安心(えっ))
dagger2でざっくりすること
- Module作ってオブジェクト生成を記述
- Component作ってインジェクション要領を宣言
- あとはspringなどDIコンテナ利用と同じ要領で必要なクラスなど用意
それぞれModuleやComponentをどういう粒度で作るのかというのはプロジェクトが始まる前に決めた方が良さそうです。共通部品(infra層、業務共通など)や個々の業務、ユースケースのある程度の塊とかで作った方がいいのかもしれません。
サンプル例
よくあるGreetingService。受け取った文字列を出力し、その際に今日の日付を出力する。dagger2に依存しない登場人物は、
- GreetingService 出力処理
- CalendarService GreetingServiceにDIされて利用される。
- GreetingCard エントリポイントから呼ばれるクラス。GreetingServiceはここにDIされて全てが始まる。
dagger2に依存する登場人物は
- GreetingModule - 生成
- GreetComponent - インジェクション
実装例
public interface GreetingService {
/** もらった文字列を今日時点として出力する */
void outAsOfToday(String word);
}
public class GreetingServiceImpl implements GreetingService {
private final CalendarService calculatorService;
@Inject GreetingServiceImpl(CalendarService calculatorService) {
this.calculatorService = calculatorService;
}
@Override
public void outAsOfToday(String word) {
LocalDate today = calculatorService.getToday();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String formattedDate = today.format(formatter);
System.out.println(String.format("Hello %s, 今日は%s日です", word, formattedDate));
}
}
public interface CalendarService {
LocalDate getToday();
}
public class CalendarServiceImpl implements CalendarService {
@Override
public LocalDate getToday() {
return LocalDate.now();
}
}
public class GreetingCard {
private final GreetingService greetingService;
@Inject GreetingCard(GreetingService greetingService) {
this.greetingService = greetingService;
}
public void print(String receiverName) {
greetingService.outAsOfToday(receiverName);
}
}
いずれもコンストラクタインジェクションで@Injectしてますがdagger2に特化したことではないので説明省略。で、これらをdagger2で動かすための記述は下記のとおり。
@Module
public class GreetingModule {
@Singleton
@Provides
GreetingService provideGreetingService(CalendarService calculatorService) {
return new GreetingServiceImpl(calculatorService);
}
@Singleton
@Provides
CalendarService provideCalculatorService() {
return new CalendarServiceImpl();
}
}
Module suffixは規約的にそうしてください、ということ。引数付きでコンストラクタインジェクションするときには上記のように書けばオK。 @Moduleの中で@Providesで提供するというもの。FactoryBeanになった気持ちで書けばいいのかな。
@Component(modules = { GreetingModule.class })
@Singleton
public interface GreetComponent {
GreetingCard greet();
}
次に依存関係を@Componentで宣言する。生成記述のModuleとここで関連付けます。このgreet()呼び出しで取得できるGreetingCardはコンパイル時に依存関係を解決にした状態でクラスたちが生成されてるようです。AnnotationConfigApplicationContextの気持ちで宣言する感じかな。
public class GreetingApp {
public static void main(String[] args) {
GreetComponent component = jp.co.gmo_aozora.ib.example.DaggerGreetComponent.builder().build();
component.greet().print("Leia");
}
}
で、最後に実行するエントリポイントの実装。ApplicationContextを初期化する気持ちでComponentをbuildします。お決まりごとでdagger2がannotation processor使って自動生成されるクラス名はDagger+Xxx(コンポーネント名)になってるようで、これがうまく生成されていない場合はお使いのIDEでannotation processor設定かdagger-compilerを認識していないかだと思われます。
実行すると
Hello Leia, 今日は2017/01/02日です
と出力されました。
次回はSubComponentとか書けたら書こうと思います。