Dagger2のProducers 基本的な使い方

square 社製の Dagger を fork して生まれた Dagger2(Google Dagger) は、リリース当初は導入事例があまり見られず手探りな状態が続いていましたが、最近ではプロダクトへの導入事例を聞くことも珍しくなくなりました。僕が関わっているいくつかのプロダクトでも Dagger2 を積極的に利用しており、もはやなくてはならない存在になったと言っても過言ではありません。

ところで、 Dagger2 は形式に沿った Module と Component のコードを書くことで、コンパイル時に依存注入するためのコードを生成し、アプリケーションが依存注入を要求した時に処理を実行するものです。この処理は同期的に行われますが、もしこの処理自体を非同期にしたい場合はどうすればよいのでしょうか。例えば、APIを叩くなどの処理が絡んでしまい、同期的に依存を解決できない場合。また依存解決に必要な処理をどうしてもメインスレッドで待つことが出来ない場合や、パフォーマンスの関係上非同期のほうが都合が良い、などといった場面の場合。我々エンジニアは、どのような書き方をすべきなのでしょうか。

その答えは既に Dagger2 に用意されています。Producers という仕組みを利用するのです。

目次

はじめに

Dagger2 には Producers という非同期で依存を解決する仕組みがあります。

通常の Inject とは違い、@Inject アノテーションを使うことはありません。 Component は ListenableFuture<T> を返すメソッドを定義するようにし、 <T> を注入して欲しいクラスはそのメソッドを呼び出します。そして非同期にインスタンスを受け取るのです。

基本的な使い方

    // dagger2
    compile 'com.google.dagger:dagger:2.5'
    compile 'com.google.dagger:dagger-producers:2.5' // これが必要
    apt 'com.google.dagger:dagger-compiler:2.5'
    androidTestApt 'com.google.dagger:dagger-compiler:2.5'
    provided 'javax.annotation:jsr250-api:1.0'

build.gradle で producers を追加する

Producers は Dagger2 を拡張したものですので、追加の dagger-producers を読み込んでおく必要があります。

簡単な例です。 String を返す Module を考えてみます。記述のポイントは以下のとおりです。

  • @ProducerModule を使うことになります。
  • userName(UserData userData) が注入するインスタンスを返すメソッドです。
  • provideUserData()UserData を生成して返しますが、そのまま返すのではなく、 ListenableFuture<UserData> を返すようにします。

@ProducerModule
final class AppProducerModule {
    @Produces
    ListenableFuture<UserData> provideUserData() {
        // ~ なんかめっちゃ時間がかかる処理 ~
        return Futures.immediateFuture(new UserData("userName"));
    }

    @Produces
    String userName(UserData userData) {
        return userData.name;
    }
}

時間がかかる処理があると仮定した AppProducerModule.java

実は Module がもう一つ必要で、それが以下に示す Executor を返す Module です。

@Module
public class ExecutorModule {
    @Provides
    @Production
    static Executor executor() {
        return Executors.newCachedThreadPool();
    }
}

ExecutorModule.java

この Executor は、非同期で依存を解決する Executor を提供するものです。

次に Component ですが、 @ProductionComponent を使います。

@ProductionComponent(modules = {ExecutorModule.class, AppProducerModule.class})
    public interface AppProducerComponent {
        ListenableFuture<String> userName();
    }

非同期依存注入で利用する AppProducerComponent.java

ExecutorModule.class はここで指定しています。

返り値が ListenableFuture<UserData> となっている点に注意してください。

注入して欲しいクラス、例えば Activity は、この Component のメソッドからインスタンスを受け取ります。

public class MainActivity extends AppCompatActivity {
    ...
        ListenableFuture<String> userDataListenableFuture = DaggerAppProducerComponent.create().userName();
        Futures.addCallback(userDataListenableFuture, new FutureCallback<String>() {
            @Override
            public void onSuccess(String result) {
                Log.e("name", result);
            }

            @Override
            public void onFailure(Throwable t) {

            }
        });
    ...
}

結果を受け取る MainActivity.java

これを実行した時、 AppProducerModule では順番にメソッドが呼ばれ、依存解決を試みます。

@ProducerModule
final class AppProducerModule {
    @Produces
    ListenableFuture<UserData> provideUserData() {
        // 1. まずはじめにここが呼ばれる。
        return Futures.immediateFuture(new UserData("userName"));
    }

    @Produces
    String userName(UserData userData) { // 1 で解決されるインスタンスが必要
        // 2. 1が終わったらここが呼ばれる。
        return userData.name;
    }

}

AppProducerModule.java 内の処理の流れ

Module は includes を使うことによって、他の Module と関係をもつことも出来ます。

@ProducerModule(includes = UserDataRequestModule.class)
final class AppProducerModule {
    @Produces
    ListenableFuture<UserData> provideUserData(APIClient client) {
        return Futures.immediateFuture(client.get()));
    }

    @Produces
    String userName(UserData userData) {
        return userData.name;
    }

}

UserDataRequestModule.class を includes する

UserDataRequestModule.classAPIClient をくれる、というイメージです。

生成されたコードでExecutorはどう使われるか

処理はすべて非同期で、 Executor を用いて行われます。先ほど ExecutorModule クラスで Executor を返すメソッドを定義しましたが、この Executor は一体どのように扱われているのでしょうか。 生成されたコードを見てみると、その答えがわかります。

public final class DaggerAppComponent implements AppComponent {
  private Provider<Executor> executorProvider;
  ...
    this.executorProvider =
        DoubleCheck.provider(
            AppComponent_ProductionExecutorModule_ExecutorFactory.create(
                ExecutorModule_ExecutorFactory.create()));
  ...

生成された DaggerAppComponent.java

Executor は Dagger によって生成されたコードの中で、 Provider<Executor> として保持されます。 この Provider はそれぞれの Factory クラス内でコンストラクタ経由で渡され、

public final class DaggerAppComponent implements AppComponent {
...
    this.getUserDataProducer =
        new AppProducerModule_GetUserDataFactory(
            builder.appProducerModule, executorProvider, monitorProvider);
...

UserData を 供給する Producer を作る AppProducerModule_GetUserDataFactory() で渡されている

Factory クラスが compute() メソッドによって処理を始めた時、 Futures の transformAsync() メソッドで非同期処理を行うときに get() メソッドを呼ぶことで Executor を得ています。

public final class AppProducerModule_GetUserDataFactory extends AbstractProducer<UserData> {
  ...
    @Override
  protected ListenableFuture<UserData> compute(final ProducerMonitor monitor) {
    return Futures.transformAsync(
        Futures.<Void>immediateFuture(null),
        new AsyncFunction<Void, UserData>() {

          @Override
          public ListenableFuture<UserData> apply(Void ignoredVoidArg) {
            monitor.methodStarting();
            try {
              return AppProducerModule_GetUserDataFactory.this.module.getUserData();
            } finally {
              monitor.methodFinished();
            }
          }
        },
        executorProvider.get());
  }

Dagger によって生成された AppProducerModule_GetUserDataFactory.java

補助的な機能

Dagger2には、様々な非同期の依存解決を実現する機能が用意されています。

Producer

先程も登場した Provider<T> と似た仕組みが Producers には用意されています。それが Producer<T> です。

まずは Module ですが、いわゆる出口であるメソッド以外で特に変わることはありません。

    @Produces
    @Normal
    ListenableFuture<UserData> provideNUserDate() {
        return Futures.immediateFuture(new UserData("normal"));
    }

    @Produces
    @Special
    ListenableFuture<UserData> provideSUserDate() {
        return Futures.immediateFuture(new UserData("special"));
    }

    @Produces
    ListenableFuture<UserData> provideUserData(@Normal Producer<UserData> nProducer, @Special Producer<UserData> sProducer) {
        return sProducer.get();
    }

Producer を受け取る provideUserData() が定義されている AppProducerModule.java

受け取り側は、インスタンスそのものを受け取る or Producer を受け取るかを決定する権利を持っています。この点は Provider<T> の性質と全く同じです。 これによって provideUserData(...) は、何かしらの処理によって依存解決処理そのものを決定できる権利を持つのです。 例えば、 Dagger2 のドキュメントの例には Flag を見て処理を分けています。

Component も、そのメソッドを使う注入先の記述も何も変わりません。手を入れるのは Module だけです。

Produced

以下の Module のコードを見てください。確実に例外が発生するコードです。

@ProducerModule
final class AppProducerModule {
    @Produces
    ListenableFuture<UserData> getUserData() {
        // ~ なんかめっちゃ時間がかかる処理 ~
        throw new IllegalStateException(); // わざと落とす
        //return Futures.immediateFuture(new UserData("userName"));
    }

    @Produces
    String userName(UserData userData) {
        return userData.name;
    }
}

例外が起こって UserData を return できない AppProducerModule.java

この時、Dagger は依存の解決を諦め、スキップを行います。これは Module 内で何か都合の悪い予期せぬ自体が起きた時、例外が起きてクラッシュするのを防ぐためであると思われます。 ただ、例外を自らの手でハンドリングしたいことも有ります。その場合は Produced<T> を使うことで実現できます。

@ProducerModule
final class AppProducerModule {
    @Produces
    ListenableFuture<UserData> getUserData() {
        // ~ なんかめっちゃ時間がかかる処理 ~
        throw new IllegalStateException(); // わざと落とす
        //return Futures.immediateFuture(new UserData("userName"));
    }

    @Produces
    String userName(Produced<UserData> userData) {
        try {
            return userData.get().name;
        } catch (ExecutionException e) {
            e.printStackTrace();
            return "何かが起きた";
        }
    }
}

必ず "何かが起きた" を返す AppProducerModule.java

スタックトレースが吐かれ、"何かが起きた"が返されます。

@IntoSet

複数のインスタンスSet<T> に保持し、依存を解決する仕組みがあります。それが @IntoSet です。 Module のメソッドに付与することで機能します。以下の例を御覧ください。

public class AppProducerMultiBindingsModule {
    @Produces
    @IntoSet
    ListenableFuture<UserData> provideNUserDate() {
        return Futures.immediateFuture(new UserData("normal"));
    }

    @Produces
    @IntoSet
    ListenableFuture<UserData> provideSUserDate() {
        return Futures.immediateFuture(new UserData("special"));
    }

    @Produces
    @IntoSet
    UserData provideDogUserDate() {
        return new UserData("dog");
    }
}

@IntoSet を付与したメソッドを定義した AppProducerMultiBindingsModule.java

Component はこのようにします。

@ProductionComponent(modules = {ExecutorModule.class, AppProducerMultiBindingsModule.class})
public interface AppProducerMultiBindingsComponent {
    ListenableFuture<Set<UserData>> getSet();
}

ListenableFuture<Set> を返すメソッドを定義した AppProducerMultiBindingsComponent.java

getSet() が呼ばれた時、関連するすべての依存の解決が試みられます。例えば上の Module の例では、すべての provide~ メソッドが、getSet() された瞬間に呼ばれるということです。インスタンスが生成され次第、Set<t> の中に追加されていきます。

ListenableFuture<Set<UserData>> set = ((App) getApplication()).getAppProducerMultiBindingsComponent().getSet();
        Futures.addCallback(set, new FutureCallback<Set<UserData>>() {
            @Override
            public void onSuccess(Set<UserData> result) {
                Log.e("success", String.valueOf(result.size())); // 3
            }

            @Override
            public void onFailure(Throwable t) {
                Log.e("error", t.toString());
            }
        });

結果を Log.e(...) で出力する MainActivity.java

すべての処理が終わり次第、 onSuccess() が呼ばれる、ということになります。

Module 内でこの Set<T> を利用して新たな値を返す技もあります。以下の例では(意味不明ですが)すべての UserData の名前を合体させて、新しい UserData を返しています。

@ProducerModule
public class AppProducerMultiBindingsModule {
    @Produces
    @IntoSet
    ListenableFuture<UserData> provideNUserDate() {
        return Futures.immediateFuture(new UserData("normal"));
    }

    @Produces
    @IntoSet
    ListenableFuture<UserData> provideSUserDate() {
        return Futures.immediateFuture(new UserData("special"));
    }

    @Produces
    @IntoSet
    UserData provideDUserDate() {
        return new UserData("dog");
    }

    @Produces UserData collect(Set<UserData> data) {
        Iterator<UserData> i = data.iterator();

        UserData normal = i.next();
        UserData special = i.next();
        UserData dog = i.next();

        return new UserData(normal.name + " and " + special.name + " and " + dog.name);
    }
}

collect() ですべての UserData.name を 足しあわせて返す AppProducerMultiBindingsModule.java

@ElementsIntoSet

Set<T> は Dagger で生成されたコードによって作られますが、 @ElementsIntoSet を使えば、複数の値を Set<T> に入れておくメソッドを自分で書くことも出来ます。

@ProducerModule
public class AppProducerMultiBindingsModule {
    @Produces
    @IntoSet
    ListenableFuture<UserData> provideNUserDate() {
        return Futures.immediateFuture(new UserData("normal"));
    }

    @Produces
    @IntoSet
    ListenableFuture<UserData> provideSUserDate() {
        return Futures.immediateFuture(new UserData("special"));
    }

    @Produces
    @IntoSet
    UserData provideDUserDate() {
        return new UserData("dog");
    }

    @Produces
    @ElementsIntoSet
    ListenableFuture<Set<UserData>> provideSet() {
        return Futures.<Set<UserData>>immediateFuture(ImmutableSet.of(new UserData("foo1")));
    }

    @Produces UserData collect(Set<UserData> data) {
        Iterator<UserData> i = data.iterator();

        UserData normal = i.next();
        UserData special = i.next();
        UserData dog = i.next();
        UserData foo1 = i.next(); // @ElementsIntoSet

        return new UserData(normal.name + " and " + special.name + " and " + dog.name);
    }
}

@ElementsIntoSet を付与した provideSet() を定義した AppProducerMultiBindingsModule.java

@IntoMap

Set<T> だけでなく Map<K, V> で扱うことも出来ます。

@MapKey
@interface Type {
    String value();
}

@ProducerModule
public class AppModule {
    @Produces
    @IntoMap
    @Type("cat")
    static ListenableFuture<UserData> cat() {
        return Futures.immediateFuture(new UserData("cat")); // このコードでは呼ばれない
    }

    @Produces
    @IntoMap
    @Type("dog")
    static ListenableFuture<UserData> dog() {
        return Futures.immediateFuture(new UserData("dog")); // このコードでは呼ばれない
    }

    @Produces
    ListenableFuture<Integer> size(Map<String, Producer<UserData>> map) {
        return Futures.immediateFuture(map.size()); // 2
    }
}

@IntoMap を使い、生成されたMapを使って Integer を返す AppModule.java

この例はすべての UserData の合計値を出しているものですが、UserData そのものを受け取っていません。受け取っているのは Producer<UserData> であり、単に map の size を得ているだけです。そのため、 cat() メソッドと dog() メソッドを動かすこと無くサイズを得ています。

まとめ

Producers を使えば、記事冒頭に書いたような要件をすべてクリアできるはずです。ちなみに Module 内のメソッドが一つ一つ非同期に流れていくさまはすこし RxJava ようではありますが、この仕組みはあくまでも非同期な依存注入を実現するためのものです。 RxJava と競合する仕組みでは勿論ありませんし、むしろ組み合わせて使うことでより利便性が向上するものであると理解しています。

効果的に使うことで大きな利益をもたらすと思われますが、まだ実際にプロダクトに導入もしておらず、僕自身も未だ深くは理解できていません。

もう少し遊んでみて、確実に効果の出せる箇所がありそうなら使っていきたいなと思っています。

より深い理解を望む場合は、Dagger2 の公式ドキュメントを御覧ください。 google.github.io

博多行って食べたもの

先週木曜ぐらいに「今週末暇だな。そういや博多行きたいんだった」と唐突に思いつき、土日使って博多に行ってきました。

 

tabelog.com

ラーメン。麺がすごく細い。ここまで細いのは珍しいようで。麺は硬めにしました。まあ分かってたけど、美味い...

 

ラーメン屋はもう一軒行きました。

tabelog.com

こちらは「豚骨ハイブリッド味玉ラーメン」。いや〜文句無しに美味い。あとここのお店に置いてあった辛子高菜も美味い!辛すぎて舌がヒリヒリしますけどね。辛いのが苦手な方は慎重になったほうがよさそう。

 

夜は飲み屋ですごしてました。

 ゴマ鯖。ゴマ鯖って知ってますか?僕は知りませんでした。「ゴマ鯖知らんのか!?」と地元民に驚かれてしまいました。どうやら博多の郷土料理のようですね。

胡麻鯖 - Wikipedia

新鮮な生の鯖にこの甘めのタレがよく合う。ここに福岡県八女市の地酒である「喜多屋」の純米大吟醸をあわせます。

 

甘みと辛味が同居している酒ですが甘みのほうが派手さがあるのでぱっと飲んだ時の印象は甘めの酒。この甘さがゴマ鯖の甘めのタレとよく合います。

www.kitaya.co.jp

 

鯵の活造り。こんなに素材の味がしっかりしている鯵は初めて食べました。長崎県からやってきたそうです。

 

かにみそとカニの身とウニを混ぜあわせた物にいくらをかけて食べるという海の旨味の塊みたいな料理。

 

ふぐの一夜干し炙り。間違いなく酒が進む一品。

 

これも福岡の地酒、天心。純米酒。酸味はちょっとしっかりめ純米酒とは思えないほど爽やか。ちょっと夏酒っぽい味だった。夕刻にひぐらしの声を聴きながら飲みたいと思える酒

 

フラフラ〜っと屋台の方にも行ってみました。一度ここで食べたかったんですよ。

 

これは間違いなくいい場所ですよね。

たことポン酢を和えたものや....

 

牛タンのステーキ。

 

手羽先の中になんと明太子を詰め込んだものも。

 

やっぱり海と山が近い街は強いですね。良い食材がいっぱいある。個人的に海が近いのが本当に羨ましい。

 

オマケにちょっとだけ観光した時の写真をどうぞ

博多はいわゆる都会な町並みなのですが、都会独特の息苦しさが全く感じられませんでした。人が少ないからなのか、建物が新しいからか、休日だったからなのか、それとも博多の人柄が街をそう見せるのか。わかりませんがここなら試しに住んでみたいなと思える街でしたね。若者もかなり多いし、未来がある面白い街だと思います。

 

次はどこに行こうかな。

初めての自動車マスツーリングで伊勢志摩へ

初めて他の車と走るという事をしました。名目は「伊勢志摩の海辺を走ろう!」というもの。

ドライブイン鳥羽で待ち合わせ。友人は四日市付近で渋滞に巻き込まれ、苦労しつつもここで合流できました。

 

腹が減ってはなんとやらという事ですぐさま「七越茶屋」で腹ごしらえ。

 

www.nanakoshi.com

手こね寿司のセットを注文しました。サザエが付いてきます。これで1500円です(税別)。

 

12時前ぐらいに入店するとガラガラでしたが、すぐさまいっぱいになりました。タイミングが良かった!

 

お腹いっぱいになった所で今度はパールロードへ。鳥羽展望台を目指します。

 

僕のクーパーS F56と友人のBMW 1 series 120i。

 

パカッ

 

どちらも2リッター直列4気筒エンジンです。F56は横置きシングルターボ、120iは縦置きNA。

 

クーパーに不満は全く無いですが、5ドアはやはり便利そうだなあと思いますね。

 

相変わらずここは景色が良い。

 

島の先にそびえるホテル?はRPGのダンジョンのよう。

 

ここから志摩の方に移動して「ツバスの鐘」があるところまで走りました。

途中で分断されてしまって先に一人で到着しました。こじんまりとしたところですね。あとなんかカメラのレンズが変なのかクーパーがぐにゃりとゆがんで映ってる気がします。

 

出世魚にあやかって願いが叶うそうです。「女をたくさんゲットする!!」とか書かれていました。頑張って欲しいです。

 

海や!!

景色は意外と良かったです。

 

もっと先に行きたかったのですが今回はこれぐらいで帰路につくことにしました。

安農SA。建物はかなり新しく、結構賑わっています。サービスランチが美味そう。

 

大内山ソフトクリーム。310円。バニラとミルクが半々位な感じでなかなか美味い。

 

5500円!!とてもじゃないけど払えません。

 

とてもじゃないけど、払えません。いつかこんなのを颯爽と買えるようになる日が来るんだろうか。

 

この日はなにげに走行距離400km超えでした。意外と海が見える道が少なくて、そこはもうちょっと工夫すべきだったかなあと思います。その分ワインディングが面白くて良かったですけどね。

 

次はどこに行こうかな。

第19回箕面ビール感謝祭に行った

言わずと知れた大阪のブリュワリー「箕面ビール」の感謝祭が開催されたので行ってきました。

http://www.minoh-beer.jp/info-message/3441.htmlwww.minoh-beer.jp


開催地は箕面ビール工場のすぐ前にあるナギノ木公園です。最寄り駅は阪急牧落駅。そこから歩いて大体15分くらい。


大体40分前に付きましたが既にかなりの人数がチケット列に並んでいました。厳密に数えたわけではないのですが、12時の販売開始頃には300人ほどいたと思います。先着100名にはプレゼントがありましたが僕は間に合わず無理でした(プレゼントは小さめのグラスでした)。


チケットは2000円でビールジョッキがついてきて3杯飲むことができます。ジョッキ無しの方はそれよりも安いですが、記念にもなるしジョッキ付きを選択しました。おっさんが苦しんでいるジョッキです。


チケット列のお次はビール受け取り列がすごい。イベント限定ビールなんかは折り返し列ができていたほどです。


サーバからビールを注いで下さいます。


これなんだっけ...確かインペリアル・スタウトだったっけ。ロースト香とコクが楽しめるビールです。うまい!でもこの暑さの中だとやっぱりさっぱりとしたIPAがいいですね。ハラキリIPA良かったですよ。


シートを敷いている方もいれば小さめのテントをはられている方もいました。


アテの出店も出ていました。かき氷も。


Tシャツの販売もありました。


グラスを購入した方はここで洗浄も可能です。

印象に残ったのは、想像以上にスタッフの方々がちゃんと仕事されていたなあということでした。今回、中規模の会場に沢山の方々があつまったわけで、適当にやっていると行列がメチャクチャになってしまうと思うんです。そんな中でちゃんと列の整理をされていたり、このビールはどこにあるかと案内をしてくれたりしていて、予想以上にしっかりと運営されていたなと感じました。

良いイベントでした。ごちそうさまでした。

ツーリングセローオイル交換

3000km走ったのでオイル交換です。


ツーリングセローなのでまずはアルミアンダーガードをソケットレンチで外します。これ取り付ける時かなり面倒くさい。アルミアンダーガードとフレームの間に隙間を設ける溝なしのナットみたいな物があって、溝がないので取り付け時、ポロポロ落ちてきます。うまいこと支えて、フレームの穴に合わせて、一つ一つ締めていかないといけません。二人がかりでやりました。


ドレンボルトより先に注油口のキャップを外します。万が一オイルを抜いた後にここが開かなければバイク屋に行くことすら出来ない。


ドレンボルトを開けてオイルを出します。左右に揺すったりフロントサスに荷重をかけたりしてオイルを抜きます。

抜き終わったらドレンボルトは優しく回していって、抵抗を感じるところまで来たらちょっとぐっと締めてそこで締めるのをやめます。エンジンはアルミなので強く締め過ぎては行けません。

オイル量は1.2L入りますが、こういうのは規定量入れると多すぎるので1.1L入れておきました。

交換した後は宇治川ライン近くにある舗装林道を走ってました。

梅雨が開けたらダートにも行きたいな。

仙禽 かぶとむし を飲んだ

素晴らしい日本酒漫画「いっぽん!!〜幸せの日本酒〜」でも紹介されているこのお酒。

昨日、急に仙禽が飲んでみたくなり、わざわざ京都の市内まで出て買いに行きました。3種類ほどありましたが気になっていたかぶとむしをチョイス。

面白いラベルですね。この「かぶとむし」は夏酒として醸されていて、毎年このぐらいの時期に出ているようです。夏酒だから、かぶとむし、ということのようです。

裏には洒落たメッセージと酒質について書かれています。僕の少年時代についてはもうすっかり忘れてしまいました。

精米歩合50%だからこれすなわち大吟醸なんですよね。しかも無濾過生原酒の中取り。しかしそれらを全く主張しないラベルにしたところは流石というか、感服いたしました。多分、そんなゴチャゴチャと難しいことよりも「かぶとむし」っていう銘柄そのものを感じて欲しいんだと思います。更にフランス語と英語の説明まで。

つまりこの酒は、日本の酒に詳しい人だけの酒じゃないんです。よく知らない日本人も、世界中の方にも向けたお酒なんですね。きっと。

さて、まずは香りから。予想通りフルーティな香り。で、飲んでみるとまず感じるのは大げさすぎるほどの酸味(思わずマジか!!と思った)。そしてぶどうのような米の旨味。第一印象は「ワイン?」と思ってしまうほどの、強い果実感を覚えます。特に酸味の扱いが上手い。こんなに強烈な酸味でも、ぶどうのような旨味がベースにあるから、上手いこと合わさって「甘酸っぱいね」という感想になるのです。いや〜面白い。

さらにこいつは原酒ですがコクはかなり抑えられており、最後には強めの辛味でキレを演出しています。これがまた暑い時期に合う。端的に言うと「さっぱりして、甘酸っぱい」清酒になっているわけです。これが夏に合わないはずが無いですね。

ただ悩んだことが一つ。それは「これ一体どの料理と合わせればいいんや...」ということです。この日は和食の日で、和食だけ作ってたのですがどうにも合わない。スモークサーモンを冷蔵庫から引っ張りだして合わせてみてもちょっと微妙。食前酒には間違いなく良いですが、実際にどんな食べ物と合わせれば良いのか。これについてはどなたかにご教授頂きたいところです。

日本酒の可能性を押し広げた面白いお酒だと思います。これからの時期に是非どうぞ。

七賢 絹の味 純米大吟醸 限定出荷品 あらばしり を飲んだ

七賢はもう僕の好みに合いすぎてて唯一無二の存在になりつつあります。

 
七賢の生酒を初めて飲んだ時、独特の透明感を感じたのを覚えています。これはスッキリしてるとか嫌なアルコール感が無いとかそんなものではなくて、もっと根本的な?部分です。たぶん水では無いかと思うのですが、正直な所この正体はよくわかってません。
 
あと七賢の生酒って、味に起伏があって面白いんです。初めは香り良くて、流行りの味かなあと思って飲むと正にそうなのですが、その後グィィッ!と辛味がやってくる。この辛味がまた絶妙で、嫌な辛さじゃない。さらに最後にはその辛さがスーッと引いていく。なんじゃこのオモロイ酒は!と感激したものでした。
 
f:id:funnelbit:20160604185145j:image
 
今回友人から頂いたこの絹の味は限定出荷品のあらばしりです。あらばしりはスッキリしてる酒が多いですが、その分旨味がどうなってるか気になる所でした。
 
猪口に入れてみると、オリが薄っすらと浮かんでいるのが見て取れます。香りは吟醸らしい華やかさが確認できます。香り抑え目にしているとラベルには書いてありましたが、結構出てますねこれは。これはワイングラスのほうが合いそう。
 
飲んでみると、ピリッとした微炭酸がきます。時同じくしてしっかりとした青りんごのような旨味。あらばしりでよくぞここまで旨味があるものです。で、その後少々の間を置いてあの丁度いい辛味。さらにその後スススッと辛味が引いてく。
 
うまい!!正に七賢の味わい。この味は七賢にしか出せんなあ。と納得の一本です。
 
ちょっと温めてみたけど、人肌ぐらいが限度かなと思います。やはり似合うのは雪冷えか冷やが良いですね。
 
更にこいつは、開封後でも冷蔵庫に入れとけばちゃんと旨味を保つんです。勿論開封前に比べたら確実に落ちますし、辛味もちょっと下品な感じになるのですが、それでも美味いんですよ。
 
優秀なプレミアム酒も良いものですがそいつらに負けない、僕の中ではむしろ勝ってる、独自の魅力を持っている一本でした。ごちそうさま。