2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Prop Tech plusAdvent Calendar 2021

Day 13

lombok こう使ってます!

Last updated at Posted at 2021-12-12

lombok とは

Java の主にクラス記述上記述が必要だけどめんどくさい細々とした定番コード(いわゆるボイラープレートコード)を、アノテーションを元にして自動生成してくれるライブラリです。

Java のコンパイラーでは、Pluggable Annotation Processing API として仕様が定義されているアノテーションプロセッサを追加することが出来ます。
Annotation Processing では、Java のアノテーションをコンパイラに対する指示として扱って、コンパイル途中に割り込んで追加のコード生成ができるようになっています。
(アノテーションプロセッサは既存のコード自体を書き換えることはできません)

一般的には元となるソースコードとは別に新しいソースコードを生成して、それも含めてコンパイル対象にするような処理がされます。

公式では次のように図示されています。

image.png
http://openjdk.java.net/groups/compiler/doc/compilation-overview/index.html より引用( License: GPLv2)

コンパイラーはアノテーションプロセッサまわりの処理を一まとまりのラウンドとして扱っていて、

1 ラウンド中に見つけたアノテーションに対応するアノテーションプロセッサを呼び出し

2 呼び出されたアノテーションプロセッサは新しいコードを生成

3 生成された新しいコードを入力としてまた新しいラウンドを開始

というのを新しいファイル生成がなくなるまで繰り返します。
これ以上、ラウンドを重ねる必要がなくなった段階で、そこまでに生成されたもの全てからクラスファイルを生成します。
(そういう意味でアノテーションプロセッサまわりの処理を a preliminary step before compilation と厳密な compilation と分けて言ったりもしています)

というのが一般的なアノテーションプロセッサの処理で lombok もアノテーションプロセッサの一種なのですが、lombok はソースコードを生成するわけではなく、コンパイル途中に生成される AST (Abstract Syntax Tree: 抽象構文木) の構造を変更することで、最終的なバイトコード生成の結果の変更を実現しています

というのも、上の図でいう Annotation Processing に入る前の Parse and Enter では、ソースを解析した結果として Syntax tree(上記の AST と同じもの) と Symbol tables が作られます。
Annotation Processing に入った段階で元となるソースに対応した AST が既にあり、そこは変更可能な状態になっているのです。
そこで lombok は新規にソースを生成するのではなく、AST (結果的には Symbol tables も)を変更しちゃうという方法を取ったようです。
(lombok の場合にはソース出力ではなく、AST の変更でラウンドが回り Symbol tables の更新がされる様子)

以上のように AST を変更するので

  • コンパイル時含めてソースとしてはまったく追加・変更がない
  • proxy とかの別クラスを介した仲介が入らないので、余計な考慮等が不要
  • 他の class から参照される際、静的な実体としてそのまま可視的(変な proxy とかかまさないし、通常の参照関係以上でも以下でもない。動的なバイトコード変更も行われない)

といったメリットがある一方で

  • ぱっとみ何やっているか分からないし、細かいところでは不安(個人的には Equals の実装なんてちょっと怖い)
  • ソースとコンパイル結果が一致しない
  • デバッガでソースを追えない
  • Eclipse(の組み込みコンパイラ ECJ)との相性が悪い
  • ↑と合わせて良く分からないトラブルに出会い勝ち

というデメリットも

特徴やメリット・デメリットについての詳しい説明は
https://web.archive.org/web/20150919015334/http://www.ibm.com/developerworks/jp/java/library/j-lombok/index.html
を参照

ともかく、lombok にはメリット・デメリット(功罪)ありますが、
今のプロジェクトではメリットを取って採用している感じです

lombok はここを見よう!

基本、公式を見れば大丈夫
https://projectlombok.org/
アノテーション毎に、どういうコード相当が生成されるのかの解説もあります

その他は検索すると色々と解説ページ(例えば Lombok 使い方メモ とか Introduction to Project Lombok)があるのでそちらを参照

import ベースで弊社ソフトウェアでの利用状況をカウントしてみた

1*** lombok.RequiredArgsConstructor
1*** lombok.NonNull

--- 凄い使っているかどうかの壁 ---

4** lombok.val
4** lombok.Data
2** lombok.Getter
2** lombok.Setter
2** lombok.Value
1** lombok.Builder
1** lombok.extern.slf4j.Slf4j
1** lombok.AllArgsConstructor
1** lombok.NoArgsConstructor
1** lombok.experimental.Delegate
8* lombok.Singular
7* lombok.EqualsAndHashCode

--- 結構使っているかどうかの壁 ---

1* lombok.experimental.UtilityClass
1* lombok.AccessLevel
1* lombok.ToString

  • lombok.Builder.Default
  • lombok.Delegate

↑ で、大体どういう風に使っているか

lombok.RequiredArgsConstructor
lombok.NonNull
→ 大体、この組み合わせで Spring のコンポーネント用
Spring 4.3以降ではコンストラクタが 1件の場合 @Autowired を書かないでも
コンストラクタインジェクションの対象になるので
この2つの組み合わせで DI 用のコンストラクタを作れて injection されるコンポーネントの Null チェックもできる
とても便利

lombok.val
→ 変数宣言時の型推論をしてくれる
が、現時点ではコンパイル時にエラーになることがあるので、今は新規には使ってない+修正時にあったら var に置き換えている

lombok.Data
→ 大体、様々なデータ保持クラス(~Resource な名称で) 用
Spring の config値保持用(@ConfigurationProperties) としても重宝

lombok.Getter
lombok.Setter
→ うーん、単純なケースでのデータ格納用クラスでの用途…かな
ただ、優先的には Data や Value の利用した方が意図が分かりやすいので、それに当てはまらないケースでという順番がいいと思われます

lombok.extern.slf4j.Slf4j
→ log 出しするところ

lombok.Value
→ Builder とか ~Constructor と一緒に使って、immutable なデータ格納用クラス

lombok.Builder
lombok.Singular
→ (主に SQL を使った DB への)検索時の条件指定用クラス(~Criteria) での利用が多い。
様々な検索用途用にプロパティとして色々と用意しているが、
毎回全部を使うわけではない immutable なオブジェクトという Criteria と Builder(パターン)の相性が良い

lombok.Singular は Criteria のメンバーとして Collection がある場合に、一オブジェクトを Collection への変換指定なしで設定を許したいとか

lombok.AllArgsConstructor
lombok.NoArgsConstructor
→ 一般的なコンストラクタ用途。静的データ格納だったりでが多そう。
一部 component でも

lombok.experimental.Delegate
lombok.Delegate
→ 指定すれば A クラスが has-a している B クラスへ処理を移譲できるようになる
効果的にいえば、外部 IF としての A クラスにBクラスのメソッドが公開されて、直接呼び出すようにできる

Resource での利用が多い(DB 上で連携しているテーブルを、クラスのメンバーとして関係を実装する際に)
楽で便利だけど痛しかゆし(もちろん ignore 指定はできるけど、予想外に外に出ちゃいがち…)

ほんとの話をいえば
外部への Map 用のクラスを作って ObjectMapper や ModelMapper で対応づけるのが丁寧で正しい道だとは思われます💦

参考資料

IBM の人による Lombok の動作解説
流し読みしかしてないけどw
Project Lombok によるカスタム AST 変換(web.archive.org で保管されてるもの)

OpenJDK のコンパイルについての Overview
Compilation Overview

アノテーションプロセッサについてはこちらなども参照
JAVAアノテーション処理とビルダーの作成
JAVA ANNOTATION PROCESSING

lombok の処理についてはこちらなども参照
カスタムLOMBOKアノテーションを実装する
アノテーションプロセッサで AST 変換 - Lombok を参考にして変数の型をコンパイル時に変更
Lombok is often used, but do you know how it works? (two)

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?