Lombokによる春のコンストラクタインジェクション

Lombokを使用したSpringでのコンストラクター注入

1. 前書き

Lombokは、定型コードを克服する非常に便利なライブラリです。 まだ慣れていない場合は、前のチュートリアル–Introduction to Project Lombokを参照することを強くお勧めします。

この記事では、SpringのConstructor-Based Dependency Injectionと組み合わせた場合の使いやすさを示します。

2. コンストラクターベースの依存性注入

constructor-based Dependency Injectionを使用してSpringの依存関係を配線する良い方法。 このアプローチでは、コンポーネントの依存関係をコンストラクターに明示的に渡す必要があります。

Field-Based Dependency Injectionとは対照的に、次のような多くの利点もあります。

  • テスト固有の構成コンポーネントを作成する必要はありません–依存関係はコンストラクターに明示的に注入されます

  • 一貫した設計–必要なすべての依存関係が強調され、コンストラクターの定義によって管理されます

  • シンプルな単体テスト– SpringFrameworkのオーバーヘッドの削減

  • finalキーワードを使用する自由を取り戻した

ただし、コンストラクターを記述する必要があるため、これを使用して大幅に大きなコードベースを作成します。 GreetingServiceFarewellService:の2つの例を考えてみましょう

@Component
public class GreetingService {

    @Autowired
    private Translator translator;

    public String produce() {
        return translator.translate("hello");
    }
}
@Component
public class FarewellService {

    private final Translator translator;

    public FarewellService(Translator translator) {
        this.translator = translator;
    }

    public String produce() {
        return translator.translate("bye");
    }
}

基本的に、両方のコンポーネントは同じことを行います。タスク固有の単語を使用して構成可能なTranslatorを呼び出します。

ただし、2番目のバリエーションは、コンストラクターの定型文がコードに実際に値をもたらさないため、はるかに難読化されています。

最新のSpringリリースでは、コンストラクターに@Autowiredアノテーションを付ける必要はありません。

3. Lombokによるコンストラクターインジェクション

Lombokを使用すると、すべてのクラスのフィールド(@AllArgsConstructorを使用)またはすべてのfinalクラスのフィールド(@RequiredArgsConstructorを使用)のコンストラクターを生成できます。 さらに、まだ空のコンストラクターが必要な場合は、追加の@NoArgsConstructorアノテーションを追加できます。

前の2つと同様に、3番目のコンポーネントを作成しましょう。

@Component
@RequiredArgsConstructor
public class ThankingService {

    private final Translator translator;

    public String produce() {
        return translator.translate("thank you");
    }
}

上記のアノテーションにより、Lombokはコンストラクターを生成します。

@Component
public class ThankingService {

    private final Translator translator;

    public String thank() {
        return translator.translate("thank you");
    }

    /* Generated by Lombok */
    public ThankingService(Translator translator) {
        this.translator = translator;
    }
}

4. 複数のコンストラクタ

コンポーネントにコンストラクターが1つしかない限り、コンストラクターに注釈を付ける必要はありません。Springは、新しいオブジェクトをインスタンス化するための適切なコンストラクターとして明確に選択できます。 さらにある場合は、IoCコンテナーで使用されるものに注釈を付ける必要もあります。

ApologizeServiceの例を考えてみましょう。

@Component
@RequiredArgsConstructor
public class ApologizeService {

    private final Translator translator;
    private final String message;

    @Autowired
    public ApologizeService(Translator translator) {
        this(translator, "sorry");
    }

    public String produce() {
        return translator.translate(message);
    }
}

上記のコンポーネントは、オプションでmessageフィールドを使用して構成できます。このフィールドは、コンポーネントの作成後に変更できません(したがって、setterがありません)。 したがって、2つのコンストラクターを提供する必要がありました。1つは完全な構成で、もう1つは暗黙のデフォルト値であるmessageです。

コンストラクターの1つに@Autowired@Inject、または@Resourceのいずれかの注釈が付けられていない限り、Springはエラーをスローします。

Failed to instantiate [...]: No default constructor found;

Lombok-で生成されたコンストラクターに注釈を付ける場合は、@AllArgsConstructoronConstructorパラメーターを使用して注釈を渡す必要があります。

@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ApologizeService {
    // ...
}

onConstructorパラメーターは、生成されたコンストラクターに配置される注釈の配列(またはこの特定の例のような単一の注釈)を受け入れます。 下位互換性の問題のため、二重下線のイディオムが導入されました。 ドキュメントによると:

奇妙な構文の理由は、この機能をjavac7コンパイラで機能させるためです。 @__タイプは、実際には存在しない注釈タイプ__(二重下線)への注釈参照です。これにより、アノテーションプロセッサが後で__タイプを作成する可能性があるため、エラーが原因でjavac7がコンパイルプロセスの中止を遅らせることになります。

5. 概要

このチュートリアルでは、ボイラープレートコードの増加に関して、コンストラクターベースのDIよりもフィールドベースのDIを優先する必要がないことを示しました。

Lombokのおかげで、ランタイムにパフォーマンスの影響を与えることなく、一般的なコード生成を自動化することができ、1行の注釈を使用することで、長くてわかりにくいコードを省略できます。

チュートリアルで使用されるコードは、over on GitHubで利用できます。