Lombok の一般的な方法と原則の紹介

ソースコードを簡素化し、プログラミング効率を向上させるツールで、一般的に使用されるコードを生成するために使用されます。

2 つのパッケージ:

  • ロンボク

  • lombok.experimental (実験的プロパティ)

ロンボクの使い方

依存関係を導入する 

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
            </dependency>

プラグインをインストールする 

で使用されるコード

@Data
public class People {
    private String name;
}

一般的な注釈のまとめ

@ヴァル

変数を宣言するとき、変数の型はそれ自体で推測でき、final 属性が付属しています。

@データ

@ToString、@EqualsAndHashCode、@Getter、@Setter和@RequiredArgsConstrutor

@Slf4j

ログオブジェクトを生成する

@価値

@Data と同様ですが、次の 2 つの違いがあります。

  • 完全なパラメーターを持つコンストラクターが生成されます。

  • getter メソッドのみで、setter メソッドはありません。

@ゲッター

すべてのプロパティのゲッター メソッド

@セッター

すべてのプロパティのセッター メソッド

@ビルダー

連鎖コンストラクターのコードを生成する

@掃除

リソースを安全に解放または閉じる。最も一般的なシナリオは、IO でストリームを閉じる操作です。

@NonNull

getter : 取得したプロパティが null の場合、NPE をスローします

setter : プロパティの設定時に渡された値が null の場合、NPE をスローします

@ToString

toString() メソッドを生成する

@SneakyThrows

スローされた例外を食べて、不要な try catch コードを減らします

@NoArgsコンストラクター

引数のないコンストラクターを生成する

@AllArgsConstructor

すべてのプロパティを持つコンストラクターを追加する

@EqualsAndHashCode

equals() および hashcode() メソッドを生成する

@RequiredArgsConstructor

NotNull とマークされた定数と変数を含むコンストラクターが生成されます

ケース

コメント部分は、ロンボクが自動的に生成するのに役立つコードです。

@Slf4j

@Slf4j
public class Test {
    // private static final Logger log=LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        log.info("Hello world");
    }
}

@ビルダー

        Test test = Test.builder()
                .id(id)
                .page(page)
                .build();

@Data
@Builder
public class Test {

    private String id;

    private String page;
    
    /**
     @java.beans.ConstructorProperties({"id", "page"})
    Test(Long id, int page) {
        this.id = id;
        this.page = page;
    }

    public static TestBuilder builder() {
        return new TestBuilder();
    }

    public TestBuilder toBuilder() {
        return new TestBuilder().id(this.id).page(this.page);
    }

    public static class TestBuilder {
        private Long id;
        private int page;

        TestBuilder() {}

        public TestBuilder id(Long id) {
            this.id = id;
            return this;
        }

        public TestBuilder page(int page) {
            this.page = page;
            return this;
        }

        public Test build() {
            return new Test(id, page);
        }

        public String toString() {
            return "Test.TestBuilder(id=" + this.id + ", page="
                + this.page")";
        }
    */
}

@SneakyThrows

    @Test(expected = RuntimeException.class)
    @SneakyThrows
    public void test_throw_exception() {
        when(HttpClientUtil.get(anyString()).thenThrow(new RuntimeException());
        api.test("nice");
    }

@データ

@Data
public class User {

    private Long id;

    private String username;

    private String password;
    
    
    /**
    public User() {}

    public Long getId() {return this.id;}

    public String getUsername() {return this.username;}

    public String getPassword() {return this.password;}

    public void setId(Long id) {this.id = id; }

    public void setUsername(String username) {this.username = username; }

    public void setPassword(String password) {this.password = password; }

    public boolean equals(Object o) {
        if (o == this) { return true; }
        ...
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $id = this.getId();
        result = result * PRIME + ($id == null ? 43 : $id.hashCode());
        final Object $username = this.getUsername();
        ...
        return result;
    }

    protected boolean canEqual(Object other) {return other instanceof User;}

    public String toString() {
        return "User(id=" + this.getId() + ...+ ")";
    }
    */
	}
}

@価値

@Value
public class Test {

    (private final) Long id;

    (private final) String page;
    
    /**
    @java.beans.ConstructorProperties({"id", "page"})
    public Test(Long id, String page) {
        this.id = id;
        this.page = page;
    }

    public Long getId() {return this.id;}

    public String getPage() {return this.page;}

    public boolean equals(Object o) {
        if (o == this) { return true; }
          ...
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $id = this.getId();
        result = result * PRIME + ($id == null ? 43 : $id.hashCode());
        ...
        return result;
    }

    public String toString() {
            return "Test.TestBuilder(id=" + this.id + ", page="
                + this.page")";
        }
    */
}

Java コード規約

  • [推奨事項] フィールドまたはコンストラクター パラメーターが多すぎる不変エンティティ クラスの場合、lombok を使用してビルダーを生成することをお勧めします

  • 【推奨事項】 レコード型データに対応する前に、エンティティクラスのフィールドはlombokアノテーションを使ってsetterとgetterを生成し、最小化の原則に従って@Getter、@Setter、@Dataのいずれかのみを使用するかを決定することをお勧めします

  • [推奨] コントラクト プログラミングでは、パラメーターが空でないことを示すために、IDEA IntelliJ > Spring > Lombok のアノテーションの空でないアノテーションを使用することをお勧めします

長所と短所

アドバンテージ

  • コード量を効果的に削減

  • 動的。たとえば、フィールドの追加や削除の際に、Getter/Setter などのメソッドの変更を気にする必要はありません。 

  • コードの可読性をある程度改善する

欠点

  • 外部依存関係を導入する. リソース パッケージで lombok が使用されると、他のユーザーがソース コードを見たい場合は、プラグインをインストールする必要があります。

  • 生成されたコードは直感的ではなく、期待どおりにコードを生成しない可能性があります

  • ソースコードの整合性の低下

よくある問題

@ビルダー

プロパティのデフォルト値の問題。@Builder を使用してコードを生成すると、属性のデフォルト値が無効になります。次のように、@Builder.Default を使用して属性にデフォルト値をマークする必要があります。

    @Builder
    public class UserQueryParam {
        private Long id;
        private String username;
        @Builder.Default
        private int page = 1;
        @Builder.Default
        private int size = 15;
    }

@ゲッター

ブール値の特別な処理

private boolean test;

1. 属性を読み取るメソッドのデフォルトの生成規則は、get+属性名ではなく is+属性名、つまり、getTest ではなく isTest です。

2. 属性名自体が is で始まる場合 (isTest など)、属性を取得するメソッドは依然として isTest であり、厄介な isIsTest ではありません。

3. 上記の 2 つのルールでは、isTest という名前の属性と test という名前の 2 つの属性が存在するという、理不尽によって引き起こされる極端な状況が発生します. これは本質的に設計上の問題です. この状況では、プラグ-in isTestを1つだけ生成する処理方法ですが、どの属性を読み込むかは属性の順番に依存し、前者が優先されます。

@Value および @Data

@Value は、不変クラスによく使用されます。不変クラスとは、クラスのインスタンスが作成された後、インスタンスのインスタンス変数を変更できないことを意味します。

@Data と同様に、主な違いが 2 つあります。

  • 完全なパラメーターを持つコンストラクターが生成されます。

  • getter メソッドのみで、setter メソッドはありません。

@価値

@データ

@ゲッター

@セッター

@ToString

@EqualsAndHashCode

@RequiredArgsConstructor

@AllArgsConstructor 

@FieldDefaults(makeFinal=true, level=AccessLevel.PRIVATE)

@Value で識別されるアノテーションは、@RequestBody および jackson と一緒に使用することはできません。

@Value と fastjson を使用した後に jackson のデシリアライズが失敗する理由は次のとおりです。

 1. Jackson は set メソッドを読み取り、@Value は set メソッドを生成しないため、失敗します。 

2. Fastjson がコンストラクターを読み取り、@Value がコンストラクターを生成するため、成功します。 

拡張: fastjson を使用する場合、@Builder を使用して、@NoArgsConstructor または @AllArgsConstructor が追加されているかどうかを確認します。

ロンボクの原理

コンパイル時に抽象構文ツリー (AST) を変更する

@Getter(AccessLevel.PUBLIC)
public class User {

    private Long id;

    private String username;

    @Getter(AccessLevel.PRIVATE)
    private String password;
}
public class User {

    private Long id;

    private String username;

    private String password;

    public Long getId() {return this.id;}

    public String getUsername() {return this.username;

    // Field 优先级更高
    private String getPassword() {return this.password;}
}

AST 構文ツリー

導入

Lombok は AST 構文ツリー リンクで処理されます. AST は、プログラム コードの文法構造を記述するために使用されるツリー表現です. 文法ツリーの各ノードは、パッケージ、タイプ、修飾子、演算子など、プログラム コードの文法構造を表します、インターフェイス、戻り値、さらにはコード コメントまで、すべて文法構造にすることができます。

サンプル

Idea では、プラグインをインストールすることで、下図のようにコードの AST 構文ツリーを表示することもできます。

左側の各属性と各メソッドは、対応する右側のノードを見つけることができるため、AST ツリーのノードを操作することで、コンパイル中にコードを動的に追加できます。

JSR269 

Java 6 以降、javac は JSR 269 Pluggable Annotation Processing API 仕様のサポートを開始しました.プログラムが API を実装している限り、Java ソース コードをコンパイルするときに、定義されたアノテーションを呼び出すことができます。Lombok の本質は、JSR 269 に依存して、Javac コンパイル段階で「アノテーション プロセッサ」(アノテーション処理ツール) の使用を実現し、カスタム アノテーションを前処理して、実際に JVM 上で実行される「クラス ファイル」を生成することです。

上記の概略図から、アノテーション処理は、コンパイラによる Java ソース コードの解析とクラス ファイルの生成の間のステップであることがわかります。

ロンボク

Lombok は、本質的に「 JSR 269 API 」を実装するプログラムですjavac を使用するプロセスでは、その機能の具体的なプロセスは次のとおりです。

  1. javac はソース コードを分析し、Abstract Syntax Tree (AST) を生成します。

  2. 運用中に「JSR269 API」を実装したLombokプログラムを呼び出す

  3. このとき、Lombok は最初のステップで取得した AST を処理し、@Data アノテーションが配置されているクラスに対応する構文ツリー (AST) を見つけ、構文ツリー (AST) を変更して、によって定義された対応するツリー ノードを追加します。ゲッターとセッターのメソッド

  4. javac は、変更された抽象構文ツリー (AST) を使用してバイトコード ファイルを生成します。つまり、新しいノード (コード ブロック) をクラスに追加します。

上記の Lombok 実行のフローチャートからわかるように、Javac が解析されて AST 抽象構文ツリーになった後、Lombok はそれ自体が記述したアノテーション プロセッサに従って動的に AST を変更し、新しいノードを追加します (つまり、Lombok カスタム アノテーションにはコードを生成する)、最後に解析によって JVM 実行可能バイトコード クラス ファイルを生成します。

コアコード

Lombok の複数のカスタム アノテーションには、対応するハンドラ処理クラスがあります. Lombok でカスタム アノテーションを実際に置換、変更、処理するのはこれらのハンドラ クラスです. 実装の詳細については、コードを参照してください.

ロンボクプラグイン

Lombok アノテーションを使用して省略されたメソッドを呼び出すと、定義が見つからないというエラーが報告されます.この場合、いくつかの特別な処理が必要です.たとえば、Intellij Idea では、Lombok プラグインをダウンロードしてインストールする必要があります.

おすすめ

転載: blog.csdn.net/xue_xiaofei/article/details/126145975