※当サイトの記事には、広告・プロモーションが含まれます。

Spring FrameworkのConstructor Injectionする際はLombokを利用するのが良さそうではあるが...

codezine.jp

 続いて、Netflix社のPaul Bakker氏、Asi Bross氏が登壇します。このサブセッションでは、Springで構築されているNetflixのエコシステムにおいて、Spring Boot 2からSpring Boot 3へのアップグレードで得られた知見が紹介されました。

Spring Framework最新情報やAIへの取り組み、Netflix事例が披露された「SpringOne 2024」レポート (1/5)|CodeZine(コードジン)

そして、Asi Bross氏がSpring Boot 3へのアップグレードの手順について詳細を解説しました。以下がアップグレード前の前提です。

  • Java 17+
  • 最新のSpring Boot 2.7系であること
  • 非推奨のコードが使われていないこと

Spring Framework最新情報やAIへの取り組み、Netflix事例が披露された「SpringOne 2024」レポート (1/5)|CodeZine(コードジン)

 アップグレードのための変更としてまずOpenRewriteやGradle Lintといったツールを用いた自動リファクタリングが実行されました。自動化で解決できない課題の一つがJavaxからJakartaへの変更処理です。

Spring Framework最新情報やAIへの取り組み、Netflix事例が披露された「SpringOne 2024」レポート (1/5)|CodeZine(コードジン)

Spring Boot 3へのアップグレードのためのJava 17+への更新、Jakartaへの更新だけでも十分に複雑であったと振り返りました。

Spring Framework最新情報やAIへの取り組み、Netflix事例が披露された「SpringOne 2024」レポート (1/5)|CodeZine(コードジン)

⇧ 大規模なシステムだとマイグレーション作業が辛過ぎるということですな...

Spring Boot 1系を使っているような大規模システムにおいては、マイグレーションするの不可能なんじゃないかという気がしてしまいすな...

Spring Frameworkにおける「依存性注入(DI:Dependency Injection)」

Spring Frameworkの公式のドキュメントによると、

docs.spring.io

⇧ Spring Frameworkにおいて「依存性注入(DI:Dependency Injection)」を実現するメジャーな方法として、

  1. Constructor-based dependency injection
  2. Setter-based dependency injection

の2つが紹介されているんだが、

docs.spring.io

⇧ APIドキュメントによると、

『Marks a constructor, field, setter method, or config method as to be autowired by Spring's dependency injection facilities. This is an alternative to the JSR-330 Inject annotation, adding required-vs-optional semantics.』

とあって、少なくとも、

  1. Autowired Constructors
  2. Autowired Fields
  3. Autowired Methods

⇧ 3つの「依存性注入(DI:Dependency Injection)」を実現する方法が用意されているらしいことが分かる。

Field Injectionが非推奨なんて話はどこから来たのか?

で、公式のドキュメントを見る限り、「Field Injection」が非推奨なんて話はどこにも出て来ないのですが、

stackoverflow.com

stackoverflow.com

medium.com

⇧ ネットの情報だと、「field injection」は推奨しないって話が出てくるんですよね。

Spring FrameworkのConstructor Injectionする際はLombokを利用するのが良さそうではあるが...

で、ネットの情報だと、

qiita.com

⇧ Lombokで「Constructor Injection」が実現できるとあったのだけど、どうも機能してくれない。

で、試しに「Field Injection」にしてみたら普通に機能しましたと。

何でなんかな、ということで参考サイト様が参考にしていたサイトを確認したところ、

pppurple.hatenablog.com

(Spring4.3以降)
Spring4.3以降ではコンストラクタ・インジェクションで@Autowiredを省略できます。
@RequiredArgsConstructorは初期化されていないfinalなフィールドをパラメータに取るコンストラクタを生成します。
@NonNullが付与されたフィールドはnullチェックが実行され、パラメータがnullの場合はNullPointerExceptionが投げられます。

SpringでField InjectionよりConstructor Injectionが推奨される理由 - abcdefg.....

⇧ なるほど、フィールドはfinalである必要があると。

www.baeldung.com

3. Constructor Injection With Lombok

With Lombok, it’s possible to generate a constructor for either all class’s fields (with @AllArgsConstructor) or all final class’s fields (with @RequiredArgsConstructor). Moreover, if you still need an empty constructor, you can append an additional @NoArgsConstructor annotation.

SpringでField InjectionよりConstructor Injectionが推奨される理由 - abcdefg.....

⇧ なるほど。

Lombokの公式のドキュメントはと言うと、

projectlombok.org

@RequiredArgsConstructor generates a constructor with 1 parameter for each field that requires special handling. All non-initialized final fields get a parameter, as well as any fields that are marked as @NonNull that aren't initialized where they are declared. For those fields marked with @NonNull, an explicit null check is also generated.

https://projectlombok.org/features/constructor

⇧ ちょいと分かり辛い...

APIドキュメントによると、

projectlombok.org

⇧ なるほど、

『Generates a constructor with required arguments. Required arguments are final fields and fields with constraints such as @NonNull.』

⇧ mustとか付いてないから勘違いしていたのだが、

  1. final fields
  2. fields with constraints such as @NonNull

の内、「1. final fields」の方についてはmustだったわけだ。

で、自分は、フィールドにfinal修飾子を付け忘れていましたと。

パッケージ構成を端折っていたり、import文を端折ってしまっていたり、プロパティファイルの内容を載せていなかったり、ファイルの配置場所なども明確にしていないのだけど、

■CustomApplicationConfig.java

@Data
//@Getter
//@AllArgsConstructor
//@RequiredArgsConstructor
@Configuration
@PropertySource("classpath:custom-application.properties")
public class CustomApplicationConfig {

  @Value("${file.upload.path}")
  private String fileUploadPath;

}    

■FileUploadService.java

@RequiredArgsConstructor
@Service
public class FileUploadService {

  private final CustomApplicationConfig customApplicationConfig;

  public boolean upload() {
    boolean isCompleteUploaded = false;
    // TODO
    
    return isCompleteUploaded;
  }

}    

⇧ という感じで機能しました。

本当は、

  @Value("${file.upload.path}")
  public static final String fileUploadPath;     

⇧ みたいにできれば他クラスから利用可能な定数にできるのだけど、

www.baeldung.com

⇧ 上記サイト様によりますと、なかなか手間がかかる感じ。

一応、

developer.mamezou-tech.com

ただし、LombokにできなくRecordクラスで出来ることが1つだけあります。それはImmutableであることの保証です。Lombokでもsetterを設けないようにすることでImmutableなインスタンスにすることができますが、それはあくまでも利用者側の使い方に委ねられています。

Java17にBump upしてみた - 使った/使わなかった新機能(後編) | 豆蔵デベロッパーサイト

対してRecoredクラスはRecoredクラスであればImmutableであることが言語仕様レベルで保証されます。ですので、DDDのValue Objectなど絶対にImmutableであることを担保したいといったオブジェクトに対し限定的に使うのには効果があると考えます。

Java17にBump upしてみた - 使った/使わなかった新機能(後編) | 豆蔵デベロッパーサイト

⇧「Lombok」を絶対に使ってはダメってわけではないということですかね。

毎度モヤモヤ感が半端ない…

今回はこのへんで。