[Java8][Framework] New Next Web Framework 「Jooby」入門 [Lifecycle]

2016.09.15

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

前回は手始めにHelloWorldでアプリケーションを起動してみました。今回から少しずつFrameworkの動作について確認していきます。今回は基本的な部分であるアプリケーションライフサイクル、アプリケーション設定、ログについて確認します。

前回と同じくJava8とMaven3が最低必須条件です。自分の環境は以下の通り。

  • Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
  • Apache Maven 3.3.9

Joobyのバージョンは以下で試しています。

  • 1.0.0.CR7

もたもたしていたらCR7が出てしまいました。

Start, Stopイベント

アプリケーション及びモジュールはそれぞれ、 start イベントと stop イベントをhookして自前の処理を記述できるようです。 初期化処理終了処理 などを記述するのが目的でしょうか。

{
    onStart(() -> {
        log.info("starting app");
    });

    onStop(() -> {
        log.info("stopping app");
    });
}

起動してみます。

[2016-08-04 14:59:37,873]-[main] INFO  jp.classmethod.App - starting app
[2016-08-04 14:59:38,213]-[main] INFO  jp.classmethod.App - [dev@netty]: Server started in 1696ms


listening on:
  http://localhost:8080/

停止してみます。

[2016-08-04 15:00:15,230]-[Thread-0] INFO  jp.classmethod.App - stopping app
[2016-08-04 15:00:15,231]-[Thread-0] INFO  jp.classmethod.App - [netty] Server stopped

Process finished with exit code 130

アプリケーションの起動時と終了時にlogが出力されているのが確認できます。全ソースは以下のとおりです。

/**
 * @author jooby generator
 */
public class App extends Jooby {

    Logger log = LoggerFactory.getLogger(App.class);

    {
        onStart(() -> {
            log.info("starting app");
        });

        onStop(() -> {
            log.info("stopping app");
        });
    }

    public static void main(final String[] args) throws Throwable {
    run(App::new, args);
  }
}

インスタンスイニシャライザ

意識せずに使っていたのですが、Joobyのサンプルではインスタンスイニシャライザが多用されています。

public class Hoge() {
    private static Logger log = LoggerFactory.getFactory(Hoge.class);

    {   // インスタンスイニシャライザでログ出力
        log.info("initialize");
    }

    /**
     * Constructor
     */
    public Hoge() {
        // コンストラクタでログ出力
        log.info("constructor");
    }
}

インスタンスイニシャライザはコンストラクタよりも先に実行される処理になります。上記コードを実行してみると initializeconstructor の順でLogが出力されます。

コンストラクタ実行前に処理されるため、インスタンス変数の初期化などに利用されることが多いようです。

Joobyの場合、routingの設定から処理の記述をlambdaで行い、Function型の変数として初期化していることから、インスタンスイニシャライザを使っているようです。コンストラクタ内で大量に初期化するよりかはとても見やすいと思います。インスタンスイニシャライザはいくつも定義できるので、処理の異なるブロックなどに分けてあげることで見やすくなるかと思います。

Lifecycle

[App] Start, Stop時に処理を実行したい

アプリケーションの起動時、終了時のイベントに合わせて何らかの処理を実行したい場合は、onStart(), onStop() を使い、実行したい処理を実装します。

public class App extends Jooby {

    Logger log = LoggerFactory.getLogger(App.class);

    // Application起動、終了時設定
    {
        onStart(() -> {
            // execute initialize
            log.info("starting app");
        });

        onStop(() -> {
            // execute before destruct
            log.info("stopping app");
        });
    }
}

起動時には starting app, 終了時には stopping app というログが出力されます。

Start, Stopの処理をChainしたい

起動時や終了時に複数の処理をシーケンシャルに実行したい場合は、 onStart(), onStop() を多重定義すれば良いようです。宣言された順序に沿って実行されるので、定義する順序は適当ではいけません。

{
        onStart(() -> {
            // execute initialize
            log.info("starting app");
        });

        onStart(() -> {
            // execute after print "starting app"
            log.info("and then Second");
        });

        onStart(() -> {
            // execute after print "and then Second"
            log.info("and then Third");
        });
}

実行すると以下の様な結果が出力されます。

[2016-08-05 18:10:35,449]-[main] INFO  jp.classmethod.App - starting app
[2016-08-05 18:10:35,449]-[main] INFO  jp.classmethod.App - and then Second
[2016-08-05 18:10:35,450]-[main] INFO  jp.classmethod.App - and then Third
[2016-08-05 18:10:36,033]-[main] INFO  jp.classmethod.App - [dev@netty]: Server started in 2738ms

starting app, and then Second, and then Third とログが表示されているのが確認できます。

[Module] Start, Stop時に処理を実行したい

Joobyではアプリケーションクラスだけではなく、ModuleクラスもLifecycleに沿って、Start, Stopイベントが実行されます。こちらも同じようにイベントに合わせて処理を実装することが可能です。

public class MyModule implements Jooby.Module {

    private static Logger logger = LoggerFactory.getLogger(MyModule.class);

    @Override
    public void configure(Env env, Config config, Binder binder) {
        env.onStart(() -> {
            logger.info("Initialize MyModule");
        });

        env.onStop(() -> {
            logger.info("Destruct MyModule");
        });
    }
}

JoobyのEnvは、アプリケーションのEnvironmentの値を管理するインタフェースです。起動引数などでEnvの値を指定し、切り替えたConfigオブジェクトに紐づく形で適宜切り替わるようです。

Lifecycleインタフェースを継承しているため、Joobyクラスと同様にModuleの起動と停止などのイベントで動作できるようです。

モジュールの読み込みはApp.javaのインスタンスイニシャライザの中で use() を使い、インスタンスを生成します。

// import Module
{
    use(new MyModule());
}

この状態で再度アプリケーションを実行してみます。

[2016-09-15 17:24:39,033]-[main] INFO  jp.classmethod.modules.MyModule - Initialize MyModule
[2016-09-15 17:24:39,034]-[main] INFO  jp.classmethod.App - starting app
[2016-09-15 17:24:39,823]-[main] INFO  jp.classmethod.App - [dev@netty]: Server started in 2325ms

停止すると以下の様なログが出力されます。

[2016-09-15 17:25:37,715]-[Thread-1] INFO  jp.classmethod.App - stopping app
[2016-09-15 17:25:37,715]-[Thread-1] INFO  jp.classmethod.modules.MyModule - Destruct MyModule
[2016-09-15 17:25:37,718]-[Thread-1] INFO  jp.classmethod.App - [netty] Server stopped
  1. モジュールの開始
  2. アプリケーションの開始
  3. アプリケーションの停止
  4. モジュールの停止

という順序で実行されるようです。

ログ

logbackが予め組み込まれていますので、特に何もせずにlogbackのクラスやメソッドは利用可能です。logbackに関して詳しいリンクはこちらへ

Logger log = LoggerFactory.getLogger(App.class);

設定ファイルは、conf/logback.xml にデフォルトで配置されています。

EnvとConfについて

アプリケーション及びモジュールは、 application.env の設定値に依存します。デフォルトでは dev が設定されています。

stage, prod 等を指定することで様々な設定値を変更することができます。アプリケーションの設定ファイル application.conf の切り替えにも依存します。 application.prod.conf 等を作成しておくことでEnvによって切り替えることができます。

所謂SpringBootのProfile切り替えに概念としては近いのではないでしょうか。Envインタフェースのオブジェクトを参照することで、現在起動中のアプリケーションがどの環境を利用してるかを把握することができます。

env.onStart(() -> {
    logger.info("Initialize MyModule. env : {}", env.name());
});

上記で特に何もせずに起動すると、以下のLogが出力されます。

[2016-09-15 18:25:56,601]-[main] INFO  jp.classmethod.modules.MyModule - Initialize MyModule. env : dev

Configインタフェースは、typesafeのライブラリです。Joobyのアプリケーション設定値は、こちらのインタフェースを通してアクセスします。 application.conf の設定値を取得する場合は、Configインターフェースのオブジェクトを利用します。

まとめ

こうやって見てみると、今まで利用していたフレームワークのライブラリをいいとこ取りで構成されています。そのため、他のフレームワーク(SpringBootやPlay)を触っていると、「あ、このクラス見覚えあるわ」とか「あ、この記述見覚えあるわ」というものがあるため、意外とさくさく読めていきます。小さなモジュールを組み上げて作成していくことが出来るようなフレームワークのようなので、こういったパーツは他にもたくさん出てきそうです。

参照