Java の Web アプリで色んなコードを共通化したい
この記事は 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 のような直接バイトコードをいじる裏技もあったりします。 ただどれもライブラリが優れているのでそんなに大変ではありません。
- JAX-RS: ExceptionMapper、フィルター、インターセプター (記事①、記事②)
- ExceptionMapper: 例外処理に特化。エラー時にエラーレスポンスを返したい時はこれ使う
- フィルター: リクエストが来る前/来た後などに処理を行う事ができる
- インターセプター:リクエストが来る前/来た後などに処理を行う事ができる
- CDI: インターセプター
- Spring:
@ControllerAdvice
,@Aspect
- Javassist でバイトコード編集
色んな場所でクエリを発行しているメソッドが増えてきた
JPQL と呼ばれる SQL っぽい JPA のクエリを使うと
- ポータブル (Mongo のような NoSQL もサポート)
- 高速
なデータアクセス層を実装できます。
まず JPA の機能について説明するのはしんどいので、詳しくはこの記事を参照してください (Spring Data JPA の例も載ってます)。
JPA では主に以下の3つの方法でクエリを実行することができます。
この内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 なんてミドルウェアもあるんですね…