きり丸の技術日記

技術検証したり、資格等をここに残していきます。

Javaで特定の文字数まで0埋めや任意の文字で埋めたい(padLeft, padRightのような動き)

小ネタ。

環境

  • Java
    • 17

対応

java.lang.Stringを使用します。内部的にはjava.util.Formatterを使用しています。

intの場合は%0{特定の文字数}d。Stringの場合は%{特定の文字数}sで半角スペースで左埋めしたあと、replace(半角スペース, 任意の文字)で半角スペースを任意の文字に書き換えます。

// 次の書き方は全て、1を0左埋めして001に書き換わります

// intを渡す場合
String.format("%03d", 1);

// Java15以降でできる書き方
"%03d".formatted(1);

// Stringを渡す場合(intでも問題ありません)
"%3s".formatted("1").replace(" ", "0")

任意の文字で右埋めしたい場合は、%{-特定の文字数}sを使用します。

// 任意の文字で1をXで右埋めして 1XXに書き換わります
"%-3s".formatted("1").replace(" ", "X")

Stringの場合、無駄なreplaceがあるように見えますが、これ以上に最適な方法が見つかりませんでした。また、半角スペースがある場合にはうまく書き換えられません。

ソースコード

終わりに

数字のIDを元に処理を進めていたのですが、S3のバケット名をIDにしようとしたときに3文字以上でなければならない制約があったのでこの方法を使用しました。

Lombokの自動生成コードのsetterにブレークポイントを張って容易にデバッグしたい

小ネタ。特定項目が処理の開始時は1だったのに、処理の終了時には1000が代入されていたとします。規模が小さいうちはSetterBuilder等々の呼び出し元にブレークポイントを張っていればいいのですが、規模が大きくなってくるとブレークポイントを張るだけでも一苦労です。

今回の記事では、Lombokの自動生成コードにブレークポイントを張って、容易にデバッグできるようにします。

環境

  • Java
    • 17
  • Lombok
    • 1.18.24
  • デコンパイルできる環境
    • IntelliJ IDEA 2022.2.3
      • FernFlower decompiler

対応

Lombokは生成先のメソッドが存在する場合にはコードを自動生成しません。Lombokによる上書き等々も発生しません。

// プロダクトコード(.java)
@Setter
public class UserDto implements Serializable {
  String userId;

  public void setUserId(final String userId) {
    System.out.println("デバッグ:" + userId);
    this.userId = userId;
  }
}
// クラスファイル(.class)
public class UserDto implements Serializable {
  String userId;

  // プロダクトコードで定義したメソッド
  public void setUserId(final String userId) {
    System.out.println("デバッグ:" + userId);
    this.userId = userId;
  }

  // 本来、Lombokから自動生成されるメソッド
  // public void setUserId(final String userId) {
  //   this.userId = userId;
  // }
}

そのため1度プロダクトコードをコンパイルし、生成されたクラスファイルからデコンパイルしたコードをプロダクトコード側に移植することで、ブレークポイントを張って容易にデバッグできます。

Setter程度なら手動作成でもいいのですが、BuilderToBuilder等々のメソッドを作るのが面倒なので、基本的にはクラスファイルからデコンパイルしたほうが楽だと思います。

ソースコード

なし。

終わりに

単純ではあるのですが、意外とやっていない人が周りに多いような気がしたのでブログにしました。こういうデバッグ作業はあまり共有されない気がしているので、誰かの役に立てば幸いです。

ただ、エンティティの項目を監視して、特定の値になったタイミングで処理を停止できるデバッグ機能の方があったらうれしいのですが…。リモートデバッグもいずれはやりたいのですが、やり方が全然ピンと来ていないので、デバッグ能力の向上もしていきたいですね。

類似情報

Lombokはいろいろ調べているので、もし興味があったらこちらの記事も見てください。

SpringのValueに初期値を与える(エラーを起こさずにnullも初期値にする)

小ネタ。@Valueで初期値を与えたい時の記法をメモします。マッピングするプロパティがない場合、BeanCreationExceptionが発生してしまうので、それが発生しないようにします。

環境

  • Java
    • 17
  • SpringBootTest
    • 2.7.4

ゴール

  • SpringのValueで初期値を渡す
  • マッピング先がない場合にExceptionではなくnullを設定する
# 次のExceptionが発生しないようにする
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kirimaru.config.SpringValueTests': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'app.config.appName2' in value "${app.config.appName2}"

対応

@Value(${placeholder:defaultValue})と記載する。

app.config.appNameにアプリケーション名を設定していた場合、@Value("${app.config.appName:defaultName}")と記載すると設定値を表示します。設定されていない場合はデフォルト値のdefaultNameを表示します。

nullを初期値に渡したい場合は、@Value("${placeholder:#{null}}")として#{null}を渡す必要があります。もし#{}を使用しない場合はnullという文字列が渡ってしまいます。

テスト

次のテストコードで動作確認しました。今回はDIしていることだけを確認したいため、例外が発生することを確認したい場合はSpringBootTestを使用してください。

@SpringJUnitConfig(SpringValueTests.Config.class)
@TestPropertySource(properties = {"app.config.appName=testApplication"})
class SpringValueTests {

  @Value("${app.config.appName:defaultName}")
  private String appName;
  @Value("${app.config.appName2:defaultName}")
  private String appName2;
  @Value("${app.config.appName3:null}")
  private String appName3;
  @Value("${app.config.appName4:#{null}}")
  private String appName4;

  @ComponentScan({"kirimaru.biz.domain.hogehoge"})
  public static class Config {}

  @Test
  void test_01() {
    SoftAssertions softly = new SoftAssertions();

    softly.assertThat(appName).isEqualTo("testApplication");
    softly.assertThat(appName2).isEqualTo("defaultName");
    softly.assertThat(appName3).isEqualTo("null");
    softly.assertThat(appName4).isNull();

    softly.assertAll();
  }
}

ソースコード

終わりに

開発環境ではproxyを経由したいが、本番環境ではproxyを経由したくない。しかし、変な設定値を設定ファイルに残したくない。

そのようなユースケースで使用しました。地味にやり方が分からなかったので、まとめられてよかったです。

類似情報