読者です 読者をやめる 読者になる 読者になる

Java の Web アプリで色んなコードを共通化したい

Java Java EE

この記事は Java EE Advent Calendar 2016 5 日目の記事です。
昨日は lbtc_xxx さんの「JPA Builder パターン」 でした。
明日は glory_of さんです。


Java の Web アプリを書く時、共通化したいけどどうすればいいか、 どう書くのが推奨されているのかをまとめてみました。 Java をガッツリやるようになったのは最近なので、間違った事を言ってたら指摘して頂けると助かります。

共通のプロパティが色んなクラスで増えてきた (CDI@Produces)

JAX-RS@Produces (javax.ws.rs.Produces) と混同しないようにしましょう。

基底クラス作って継承するとなると、基底クラスが fat になりがちで、テストする時に対象のメソッド以外にも色々セットアップしなきゃならなくなったりして面倒です。 他の言語だったら mix-in や trait で解決?するのかもしれませんが、Java では DI で解決すると楽です。

Java EEのCDIで定義しておくと便利なプロデューサーとインターセプタ - きしだのはてな

同じ処理をしているメソッドが増えてきた

メソッドをインターセプトして前処理や後処理を行います。 メソッドの中身で行っている中間の処理を共通化したい場合はメソッドの共通化や DI を検討しましょう。

やりすぎるとメンテナンスが辛くなりますが、具体的に使われるのは以下のようなユースケースだと思います。

  • 共通の例外処理 (エラーコードのレスポンスを送る)
  • 共通のロギング処理 (コントローラーやサービスのメソッドが呼び出される前後にロギングする)
  • 共通の認証処理

この手の AOP っぽい処理は大体フレームワークでサポートされていて、 果ては Javassist のような直接バイトコードをいじる裏技もあったりします。 ただどれもライブラリが優れているのでそんなに大変ではありません。

色んな場所でクエリを発行しているメソッドが増えてきた

JPQL と呼ばれる SQL っぽい JPA のクエリを使うと

  • ポータブル (Mongo のような NoSQL もサポート)
  • 高速

なデータアクセス層を実装できます。

まず JPA の機能について説明するのはしんどいので、詳しくはこの記事を参照してください (Spring Data JPA の例も載ってます)。

JPA では主に以下の3つの方法でクエリを実行することができます。

  1. JPQL
  2. ネイティブSQL
  3. Criteria API

この内1と2のクエリには @NamedQuery (JPQL)、@NamedNativeQuery (ネイティブSQL) で名前を付ける事ができます。

@NamedQuery(name="hello.findAll", query="select h from HelloEntity h")

名前を付ける事によって、複雑なクエリ以外は Dao クラスを作る必要もなく、エンティティにクエリを集約できます。

上記のアノテーションだけだと複数指定ができないので、 それぞれ @NamedQueries (JPQL)、@NamedNativeQueries (ネイティブSQL) と組み合わせる事で複数定義ができます。

@NamedQueries({
    @NamedQuery(name="hello.findAll", query="select h from HelloEntity h"),
    @NamedQuery(name="hello.findOne", query="select h from HelloEntity h where h.lang = :lang"),
    @NamedQuery(name="hello.deleteMsg", query="delete from HelloEntity h where h.lang = :lang")
})

ちなみに自分は他のエンティティと衝突しないよう、上記のように「(テーブル名).(クエリ名)」のような名前を付けています。

アノテーションがごちゃごちゃしてきた (CDI@Stereotype)

これまでにアノテーションが沢山出てきましたが、アノテーションがごちゃごちゃしてきたら、 @Stereotype アノテーションでいくつかのアノテーションをまとめて定義できます。

CDIのStereotypes - Challenge Java EE !

XML ファイルが多い

沢山の XML ファイルをコードで集約する、というのも一つの共通化かもしれません。

共通処理をマイクロサービスで行う?

マイクロサービスよく知りませんが、API Gateway Pattern というので上掲したような共通処理を担うサービスを作る、という考えがあるようです (強調は私です)。 Kong なんてミドルウェアもあるんですね…

  • 一箇所見に行けば全てのAPIを見つけられる
  • 細かい権限管理も可能
  • APIで何回も実装しないといけない部分を省略できる
    • Authentication
    • Rate Limiting
    • Aggregation
    • アクセス分析
    • ルーティング
    • データ変換
    • etc...

綺麗なAPI速習会 - Qiita

その他の参考リンク