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を使用するプロジェクトの |
(2)
|
Lombokはアプリケーション実行時には必要ないライブラリなので、スコープは |
Note
上記設定例は、依存ライブラリのバージョンを親プロジェクトであるterasoluna-gfw-parent
で管理する前提であるため、pom.xmlでのバージョンの指定は不要である。
上記の依存ライブラリはterasoluna-gfw-parentが依存しているSpring Bootで管理されている。
12.2.3.2. IDE連携¶
LombokをIDE上で使用する場合は、IDEが提供するコンパイル(ビルド)機能と連携するために、LombokをIDEにインストールする必要がある。
12.2.3.2.1. Lombokのダウンロード¶
Lombokのjarファイルをダウンロードする。
Lombokのjarファイルは、
Mavenのローカルリポジトリ(通常は、
$HOME/.m2/repository/org/projectlombok/lombok/<version>/lombok-<version>.jar
)
から取得する。
12.2.3.2.2. Lombokのインストール¶
ダウンロードしたLombokのjarファイルを実行(ダブルクリック)し、インストーラーを立ち上げる。
Lombokをインストールした後にSTSを起動(又は再起動)すると、STS上でLombokを使用して開発する事ができる。
12.2.4. Lombokの使用方法¶
ここからは、Lombokの具体的な使い方について説明していく。
12.2.4.1. Lombokが提供しているアノテーション¶
まず、Lombokが提供する代表的なアノテーションを紹介する。
各アノテーションの詳細な使用方法や、本ガイドラインで紹介していないアノテーションの使い方については、
を参照されたい。
項番 |
アノテーション |
説明 |
---|---|---|
getterメソッドを生成するためのアノテーション。 クラスレベルにアノテーションを指定すると、全てのフィールドにgetterメソッドを生成する事ができる。 |
||
setterメソッドを生成するためのアノテーション。 クラスレベルにアノテーションを指定すると、全ての非finalフィールドにsetterメソッドを生成する事ができる。 |
||
|
||
|
||
初期化が必要なフィールド(finalフィールドなど)の初期化パラメータを引数に持つコンストラクタを生成するためのアノテーション。 全てのフィールドが任意のフィールドの場合は、デフォルトコンストラクタ(引数なしのコンストラクタ)が生成される。 |
||
全てのフィールドの初期化パラメータを引数に持つコンストラクタを生成するためのアノテーション。 |
||
デフォルトコンストラクタを生成するためのアノテーション。 |
||
|
||
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)
|
クラスレベルに |
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);
// omitted
}
以下に、フィールドの初期値を指定するコンストラクタを生成する方法を示す。
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)
// omitted
}
項番 |
説明 |
---|---|
(1)
|
クラスレベルに |
(2)
|
クラスレベルに JavaBeanとして使用する場合は、デフォルトコンストラクタも生成しておく必要がある。 |
(3)
|
フィールドの初期値を指定するコンストラクタを呼び出し、JavaBeanのインスタンスを生成する。 デフォルトコンストラクタを使用した場合は3ステップ必要だったものが、1ステップでインスタンスの生成が出来るようになった。 |
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) {
logger.info("{} had tried login.", userId);
// omitted
}
}
以下に、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)
// omitted
}
}
項番 |
説明 |
---|---|
(1)
|
クラスレベルに 本ガイドラインでは、SLF4Jの デフォルトでは、アノテーションを付与したクラスのFQCN(上記例だと |
(2)
|
Lombokによって生成されたSLF4Jのロガーインスタンスのメソッドを呼び出し、ログを出力する。 上記例では、
というログが出力される。 |
Tip
デフォルトで使用されるロガー名を変更したい場合は、@Slf4j
アノテーションのtopic
属性に、任意のロガー名を指定すればよい。
12.2.5. MapStructとの併用¶
MapStructとLombokを併用する場合はmaven-compiler-plugin
に設定が必要となる。
詳しくは、Lombokを使用する際の設定を参照されたい。