12.2. ボイラープレートコードの排除(Lombok)¶
目次
12.2.1. Lombokとは¶
Lombok は、 Java言語におけるボイラープレートコードをソースコードから排除するために使用するライブラリである。
ボイラープレートコードとは、言語仕様上省く事ができない定型的なコードの事である。 ボイラープレートコードは本質的なロジックでないため、アプリケーションを実装する上で冗長なコードとなる。
Java言語における代表的なボイラープレートコードには、
- メンバー変数にアクセスするための getter / setter メソッド
equals
/hashCode
メソッドtoString
メソッド- コンストラクタ
- リソース(入出力ストリーム等)のクローズ処理
- ロガーインスタンスの生成
等がある。
Lombokは、これらのボイラープレートコードをコンパイル時に生成することで、 開発者が実装するソースコード上から冗長なコードを取り除く仕組みを提供している。
Tip
リソース(入出力ストリーム等)のクローズ処理については、Java SE7から追加されたtry-with-resources文を使う事で、 ボイラープレートコードにならないように言語仕様が改善されている。
Java言語自体もバージョンアップする毎に、冗長なコードを記載しなくて済むように改善されている。 Java SE8からサポートされたラムダ式は、代表的な言語仕様の改善と言える。
12.2.2. Lombokの効果¶
以下に、Lombokを使用して作成したJavaBeanのソースコードを示す。
package com.example.domain.model;
@lombok.Data
public class User {
private String userId;
private String password;
}
クラスレベルに@lombok.Data
アノテーションを付与するだけで、
JavaBeanとして必要なメソッドがLombokによって生成される。
これは、Lombokの@Data
アノテーションを付与しただけで、約10行のソースコードから、
約60行ある下記のソースコード(Eclipseの自動生成機能を使用して出力したソースコード)によって生成されるクラスと同じ効果を得る事ができる事を意味している。
package com.example.domain.model;
public class User {
private String userId;
private String password;
public User() {
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((password == null) ? 0 : password.hashCode());
result = prime * result + ((userId == null) ? 0 : userId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (password == null) {
if (other.password != null)
return false;
} else if (!password.equals(other.password))
return false;
if (userId == null) {
if (other.userId != null)
return false;
} else if (!userId.equals(other.userId))
return false;
return true;
}
@Override
public String toString() {
return "User [userId=" + userId + ", password=" + password + "]";
}
}
12.2.3. Lombokのセットアップ¶
12.2.3.1. 依存ライブラリの追加¶
Lombokが提供しているクラスを使用するために、Lombokを依存ライブラリとして追加する。
<!-- (1) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope> <!-- (2) -->
</dependency>
項番 | 説明 |
---|---|
(1)
|
Lombokを使用するプロジェクトの pom.xml に、Lombokを依存ライブラリとして追加する。 |
(2)
|
Lombokはアプリケーション実行時には必要ないライブラリなので、スコープはprovided が適切である。 |
Note
上記設定例は、依存ライブラリのバージョンを親プロジェクトである terasoluna-gfw-parent で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。 上記の依存ライブラリはterasoluna-gfw-parentが利用しているSpring IO Platformで定義済みである。
12.2.3.2. IDE連携¶
LombokをIDE上で使用する場合は、IDEが提供するコンパイル(ビルド)機能と連携するために、 LombokをIDEにインストールする必要がある。
本ガイドラインでは、Spring Tool Suite(以降、「STS」と呼ぶ)にインストールする方法を紹介する。 使用するIDEによってインストール方法は異なるため、 STS以外のIDEを使用する場合は、 こちらのページ を参考にされたい。
12.2.3.2.1. Lombokのダウンロード¶
Lombokのjarファイルをダウンロードする。
Lombokのjarファイルは、
- Lombokのダウンロードページ
- Mavenのローカルリポジトリ(通常は、
$HOME/.m2/repository/org/projectlombok/lombok/<version>/lombok-<version>.jar
)
から取得する。
12.2.3.2.2. Lombokのインストール¶
ダウンロードしたLombokのjarファイルを実行(ダブルクリック)し、インストーラーを立ち上げる。
インストール対象のSTSを選択後、”Install / Update” ボタンを押下してインストールを実行する。 インストール候補のSTSは、インストーラーによって自動検出される仕組みになっているが、 自動で検出されない場合は、”Specify location …”を押下してIDEを指定する必要がある。
Lombokをインストールした後にSTSを起動(又は再起動)すると、STS上でLombokを使用して開発する事ができる。
12.2.4. Lombokの使用方法¶
ここからは、Lombokの具体的な使い方について説明していく。
Lombokを初めて使用する場合は、まず、 Lombokの「 Demo Video 」を参照するとよい。 Demo Videoは4分弱で構成されており、最も基本的な使い方が説明されている。
12.2.4.1. Lombokが提供しているアノテーション¶
まず、Lombokが提供する代表的なアノテーションを紹介する。
各アノテーションの詳細な使用方法や、本ガイドラインで紹介していないアノテーションの使い方については、
を参照されたい。
項番 | アノテーション | 説明 |
---|---|---|
@lombok.Getter | getterメソッドを生成するためのアノテーション。 クラスレベルにアノテーションを指定すると、全てのフィールドにgetterメソッドを生成する事ができる。 |
|
@lombok.Setter | setterメソッドを生成するためのアノテーション。 クラスレベルにアノテーションを指定すると、全ての非finalフィールドにsetterメソッドを生成する事ができる。 |
|
@lombok.ToString | toString メソッドを生成するためのアノテーション。 |
|
@lombok.EqualsAndHashCode | equals とhashCode メソッドを生成するためのアノテーション。 |
|
@lombok.RequiredArgsConstructor | 初期化が必要なフィールド(finalフィールドなど)の初期化パラメータを引数に持つコンストラクタを生成するためのアノテーション。 全てのフィールドが任意のフィールドの場合は、デフォルトコンストラクタ(引数なしのコンストラクタ)が生成される。 |
|
@lombok.AllArgsConstructor | 全てのフィールドの初期化パラメータを引数に持つコンストラクタを生成するためのアノテーション。 | |
@lombok.NoArgsConstructor | デフォルトコンストラクタを生成するためのアノテーション。 | |
@lombok.Data |
|
|
@lombok.extern.slf4j.Slf4j | SLF4Jのロガーインスタンスを生成するためのアノテーション。 |
12.2.4.2. JavaBeanの作成¶
本ガイドラインが推奨する方法でアプリケーションを構築した場合、
- Formクラス
- Resourceクラス(REST API構築時)
- Entityクラス
- DTOクラス
などのJavaBeanを作成する必要がある。
以下に、JavaBeanの作成例を示す。
package com.example.domain.model;
import lombok.Data;
@Data // (1)
public class User {
private String userId;
private String password;
}
項番 | 説明 |
---|---|
(1)
|
クラスレベルに、
を生成する。 |
12.2.4.2.1. toStringの対象から特定のフィールドを除外する方法¶
オブジェクトの状態を文字列に変換する際は、
- 相互参照関係をもつオブジェクトを保持するフィールド
- 個人情報やパスワードなどの機密情報を保持するフィールド
などを文字列変換の対象から除外する事が必要になるケースがある。 これらのフィールドを変換対象から除外しない場合、
- 前者は、循環参照となり
StackOverflowError
やOutOfMemoryError
などが発生する - 後者は、変換後の文字列の使用方法によっては、個人情報の漏洩に繋がる
可能性があるので、注意が必要である。
Warning
JPAのEntityクラスに@Data
や@ToString
アノテーションを使用する場合は、
循環参照になりやすいので特に注意が必要である。
以下に、特定のフィールドを文字列変換の対象から除外する方法を示す。
package com.example.domain.model;
import lombok.Data;
import lombok.ToString;
@Data
@ToString(exclude = "password") // (1)
public class User {
private String userId;
private String password;
}
項番 | 説明 |
---|---|
(1)
|
クラスレベルに 上記例のソースコードから生成されたクラスの
という文字列に変換される。 |
12.2.4.2.2. equalsとhashCodeの対象から特定のフィールドを除外する方法¶
Lombokのアノテーションを使用してequals
メソッドとhashCode
メソッドを作成する場合は、
相互参照関係をもつオブジェクトを保持するフィールドを除外して生成する必要がある。
これらのフィールドを除外せずに生成した場合、
循環参照となりStackOverflowError
や OutOfMemoryError
などが発生するので、注意が必要である。
Warning
JPAのEntityクラスにData
アノテーション、Value
アノテーション、@EqualsAndHash
を使用する場合は、
循環参照になりやすいので特に注意が必要である。
以下に、特定のフィールドを除外する方法を示す。
package com.example.domain.model;
import java.util.List;
import lombok.Data;
@Data
public class Order {
private String orderId;
private List<OrderLine> orderLines;
}
package com.example.domain.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Data
@ToString(exclude = "order")
@EqualsAndHashCode(exclude = "order") // (1)
public class OrderLine {
private Order order;
private String itemCode;
private int quantity;
}
項番 | 説明 |
---|---|
(1)
|
クラスレベルに@EqualsAndHashCode アノテーションを指定し、exclude 属性に除外したいフィールド名を列挙する。 |
Tip
除外するフィールドを指定するのではなく、特定のフィールドのみを使用するように指定することもできる。
@Data @ToString(exclude = "order") @EqualsAndHashCode(of = "itemCode") // (2) public class OrderLine { private final Order order; private final String itemCode; private final int quantity; }
項番 説明 (2)特定のフィールドのみを使用する場合は、
@EqualsAndHashCode
アノテーションのof
属性に対象のフィールド名を列挙する。上記例では、
itemCode
フィールドのみを参照して処理を行うequals
メソッドとhashCode
メソッドが生成される。
12.2.4.2.3. フィールド初期化用のコンストラクタを生成する方法¶
アプリケーションの実装コードからJavaBeanのインスタンスを生成する場合は、 フィールドの初期値を引数に渡す事ができるコンストラクタがあった方が便利であり、 冗長なコードを排除することもできる。
デフォルトコンストラクタを使用してインスタンスを生成した場合は、以下のようなコードとなる。
public void login(String userId, String password) {
User user = new User();
user.setUserId(userId);
user.setPassword(password);
// ...
}
以下に、フィールドの初期値を指定するコンストラクタを生成する方法を示す。
package com.example.domain.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor // (1)
@NoArgsConstructor // (2)
@ToString(exclude = "password")
public class User {
private String userId;
private String password;
}
public void login(String userId, String password) {
User user = new User(userId, password); // (3)
// ...
}
項番 | 説明 |
---|---|
(1)
|
クラスレベルに@AllArgsConstructor アノテーションを指定し、全てのフィールドの初期値を引数にとるコンストラクタを生成する。 |
(2)
|
クラスレベルに JavaBeanとして使用する場合は、デフォルトコンストラクタも生成しておく必要がある。 |
(3)
|
フィールドの初期値を指定するコンストラクタを呼び出し、JavaBeanのインスタンスを生成する。 デフォルトコンストラクタを使用した場合は3ステップ必要だったものが、 1ステップでインスタンスの生成が出来るようになった。 |
Warning
@Dataと@NoArgsConstructorを付与する順序について
TERASOLUNA Framework 5.4.2.RELEASEが利用するLombok 1.16.22では、@Data
と@NoArgsConstructor
の付与順序によってはコンパイルエラーが発生するという事象が確認されている。
具体的には、@Data
により生成されたデフォルトコンストラクタを@NoArgsConstructor
が再度生成することによりコンパイルエラーになる事象で、Lombok 1.18.0で修正されている。
この事象を回避するには、@NoArgsConstructor
を@Data
より上に付与すれば良い。
また、Lombokのバージョンを1.18.0以上に上げることでも回避可能である。
詳しくは、Lombok-1.16.22-constructor already defined in class を参照されたい。
Tip
上記例で扱っているUser
クラスを、JavaBeanではなく、Immutableなクラスにしたい場合は、
@lombok.Value
アノテーションを使用するとよい。
@Value
アノテーションについては、Lombokのリファレンス を参照されたい。
12.2.4.3. ロガーインスタンスの作成¶
デバッグログやアプリケーションログを出力するために、ロガーインスタンスを生成する必要がある場合は、 ロガーインスタンスを生成するためのアノテーションを使用するとよい。
Lombokのアノテーションを使用しないでロガーインスタンスを作成する場合は、以下のようなコードになる。
package com.example.domain.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class AuthenticationService {
private static final Logger logger = LoggerFactory.getLogger(AuthenticationService.class);
public void login(String userId, String password) {
log.info("{} had tried login.", userId);
// ...
}
}
以下に、Lombokのアノテーションを使用してロガーインスタンスを作成する方法を示す。
package com.example.domain.service;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;
@Slf4j // (1)
@Service
public class AuthenticationService {
public void login(String userId, String password) {
log.info("{} had tried login.", userId); // (2)
// ...
}
}
項番 | 説明 |
---|---|
(1)
|
クラスレベルに 本ガイドラインでは、SLF4Jの デフォルトでは、アノテーションを付与したクラスのFQCN(上記例だと |
(2)
|
Lombokによって生成されたSLF4Jのロガーインスタンスのメソッドを呼び出し、ログを出力する。 上記例では、
というログが出力される。 |
Tip
デフォルトで使用されるロガー名を変更したい場合は、
@Slf4j
アノテーションのtopic
属性に、任意のロガー名を指定すればよい。