Java Tips | 基本ユーティリティ:システムプロパティ取得

Java Java
スポンサーリンク

システムプロパティ取得は「実行環境の設定をコードから読む」技

業務システムでは、「本番と検証で挙動を変えたい」「OS や Java のバージョンをログに出したい」「起動オプションで渡した値を読みたい」といった場面がよくあります。
こういう「実行環境に紐づく設定」を、Java から読むための仕組みが「システムプロパティ」です。

System.getProperty(...) 自体はとてもシンプルですが、そのままベタ書きすると「キー名の typo」「null の扱い」「デフォルト値の決め方」がバラバラになりがちです。
そこで、「システムプロパティ取得」をユーティリティとしてまとめておくと、実務でかなり扱いやすくなります。


システムプロパティの基本と、どこから来る値なのか

System.getProperty の基本的な使い方

一番シンプルな例から見てみます。

public class SystemPropertyBasic {

    public static void main(String[] args) {
        String javaVersion = System.getProperty("java.version");
        String userHome    = System.getProperty("user.home");

        System.out.println("java.version = " + javaVersion);
        System.out.println("user.home    = " + userHome);
    }
}
Java

"java.version""user.home" のようなキーは、JVM が起動時に自動でセットしてくれる代表的なシステムプロパティです。
これ以外にも、-Dkey=value という形で JVM 起動オプションから任意のプロパティを渡すことができます。

java -Dapp.env=prod -DfeatureX.enabled=true MyApp
Java

この場合、Java 側ではこう読めます。

String env = System.getProperty("app.env");              // "prod"
String featureX = System.getProperty("featureX.enabled"); // "true"
Java

ここで大事なのは、「システムプロパティは“文字列のマップ”であり、すべて String として扱われる」という感覚です。
真偽値や数値として使いたい場合は、自分でパースする必要があります。

システムプロパティと環境変数の違い

よく混同されるのが「システムプロパティ」と「環境変数」です。

環境変数は OS 側の設定で、System.getenv("NAME") で取得します。
システムプロパティは JVM 内の設定で、System.getProperty("name") で取得します。

どちらを使うかはプロジェクトの方針次第ですが、「Java の世界で完結した設定(起動オプションで渡すなど)」はシステムプロパティ、「インフラ側で管理する設定」は環境変数、という分け方をすることが多いです。


実務で使える「システムプロパティ取得ユーティリティ」の最小形

null と空文字、デフォルト値を一箇所で扱う

System.getProperty は、キーが存在しないと null を返します。
これを呼び出し側で毎回 null チェックするのは面倒なので、ユーティリティに閉じ込めてしまいます。

public final class SysProps {

    private SysProps() {}

    public static String get(String key) {
        return System.getProperty(key);
    }

    public static String getOrDefault(String key, String defaultValue) {
        String value = System.getProperty(key);
        return (value != null) ? value : defaultValue;
    }

    public static boolean getBoolean(String key, boolean defaultValue) {
        String value = System.getProperty(key);
        if (value == null) {
            return defaultValue;
        }
        return Boolean.parseBoolean(value);
    }

    public static int getInt(String key, int defaultValue) {
        String value = System.getProperty(key);
        if (value == null) {
            return defaultValue;
        }
        try {
            return Integer.parseInt(value);
        } catch (NumberFormatException e) {
            return defaultValue;
        }
    }
}
Java

使う側はこう書けます。

String env = SysProps.getOrDefault("app.env", "dev");
boolean featureX = SysProps.getBoolean("featureX.enabled", false);
int maxConnections = SysProps.getInt("db.maxConnections", 10);
Java

ここで深掘りしたいポイントは三つです。

一つ目は、「デフォルト値の決め方をユーティリティ側に寄せている」ことです。
「設定がなければ dev とみなす」「設定がなければ false とみなす」といったポリシーを、呼び出し側ではなくユーティリティの呼び出しで明示できます。

二つ目は、「文字列→boolean/int のパースを一箇所に集約している」ことです。
Boolean.parseBooleanInteger.parseInt をあちこちに書くより、getBooleangetInt に閉じ込めたほうが読みやすく、バグも減ります。

三つ目は、「パースに失敗したときの扱い(NumberFormatException のときはデフォルト値に戻す)」を統一していることです。
ここを「例外を投げる」にするか「デフォルトに戻す」にするかも、ユーティリティ側で方針を決めておくと、アプリ全体で一貫性が出ます。


「必須プロパティ」と「任意プロパティ」を分けて扱う

必須プロパティは「なければ即エラー」にする

例えば、「本番環境では必ず app.env が設定されていなければならない」といった「必須プロパティ」があります。
これを null のまま進めてしまうと、後でよく分からないバグになります。

そこで、「必須プロパティを取得するメソッド」を用意します。

public static String getRequired(String key) {
    String value = System.getProperty(key);
    if (value == null || value.isBlank()) {
        throw new IllegalStateException("Required system property is missing: " + key);
    }
    return value;
}
Java

使う側はこうです。

String env = SysProps.getRequired("app.env");
Java

ここでの重要ポイントは、「“必須かどうか”を呼び出し側の if 文ではなく、メソッド名で表現している」ことです。
getRequired と書かれていれば、「ここで設定がなければ起動時に落ちる」という意図が一目で分かります。

任意プロパティは「デフォルト付き」で扱う

一方、「あれば使うけど、なければデフォルトでいい」という任意プロパティは、先ほどの getOrDefaultgetBooleangetInt で十分です。
この「必須/任意」の切り分けを、ユーティリティのメソッドレベルで表現しておくと、設定の意味がコードから読み取りやすくなります。


よく使うキーを「定数」としてまとめる

マジックストリングを減らす

"app.env""featureX.enabled" のようなキー文字列を、コードのあちこちにベタ書きすると、typo や変更漏れの原因になります。
そこで、「システムプロパティのキーを集約したクラス」を作るのが定番です。

public final class SysPropKeys {

    private SysPropKeys() {}

    public static final String APP_ENV = "app.env";
    public static final String FEATURE_X_ENABLED = "featureX.enabled";
    public static final String DB_MAX_CONNECTIONS = "db.maxConnections";
}
Java

使う側はこうなります。

String env = SysProps.getOrDefault(SysPropKeys.APP_ENV, "dev");
boolean featureX = SysProps.getBoolean(SysPropKeys.FEATURE_X_ENABLED, false);
Java

ここで深掘りしたいのは、「キー名を“意味のある定数名”にすることで、設定の意図がコードから伝わる」という点です。
"app.env" だけ見ても何のことか分かりにくいですが、SysPropKeys.APP_ENV なら「アプリケーションの環境を表すキーだな」とすぐに分かります。


システムプロパティをログに出しておくという実務テク

起動時に「重要なプロパティ」をまとめてログ出力する

本番でトラブルが起きたとき、「このプロセスはどんな設定で動いていたのか」が分からないと、原因調査が難しくなります。
そこで、アプリ起動時に「重要なシステムプロパティ」をまとめてログに出しておくのは、実務でよく効くテクニックです。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class SysPropLogger {

    private static final Logger log = LoggerFactory.getLogger(SysPropLogger.class);

    private SysPropLogger() {}

    public static void logStartupProperties() {
        log.info("System properties at startup: app.env={}, featureX.enabled={}, db.maxConnections={}",
                SysProps.getOrDefault(SysPropKeys.APP_ENV, "dev"),
                SysProps.getBoolean(SysPropKeys.FEATURE_X_ENABLED, false),
                SysProps.getInt(SysPropKeys.DB_MAX_CONNECTIONS, 10)
        );
    }
}
Java

起動時に一度だけこう呼びます。

SysPropLogger.logStartupProperties();
Java

これで、「どの環境で」「どんな設定値で」動いていたかがログから追えるようになります。
ここでのポイントは、「設定値を“使うときだけ”でなく、“起動時にまとめて記録しておく”」という発想です。


システムプロパティ取得で気をつけるべきこと

セキュリティ上の注意(パスワードなど)

システムプロパティには、DB パスワードや API キーなど、機密情報を載せることもできます。
ただし、それをそのままログに出したり、画面に表示したりすると、情報漏えいのリスクになります。

ユーティリティ側で「マスクする」仕組みを用意するのも一つの手です。

public static String masked(String value) {
    if (value == null || value.isEmpty()) {
        return "(empty)";
    }
    return "****";
}
Java

ログ出力時にこう使います。

log.info("db.password={}", SysProps.masked(System.getProperty("db.password")));
Java

「どのプロパティはログに出してよいか」「どれはマスクすべきか」は、チームでルールを決めておくと安全です。

テスト時にシステムプロパティを上書きする

テストコードでシステムプロパティを使う場合、System.setProperty で一時的に上書きすることがあります。

System.setProperty("app.env", "test");
try {
    // テスト実行
} finally {
    System.clearProperty("app.env");
}
Java

ユーティリティを使う前提なら、「テスト用にプロパティをセットするヘルパー」を用意しておくと、後始末を忘れにくくなります。


まとめ:システムプロパティ取得ユーティリティで身につけるべき感覚

システムプロパティ取得は、「ただ文字列を読む」だけではなく、「設定の意味・必須/任意・デフォルト値・セキュリティ」を設計する行為です。

押さえておきたいポイントは次の通りです。

System.getProperty をそのままばらまかず、SysProps のようなユーティリティに閉じ込める。
文字列だけでなく、boolean/int などへの変換と、パース失敗時の扱いを一箇所で決める。
「必須プロパティ」と「任意プロパティ」をメソッドレベルで分け、設定漏れは起動時に気づけるようにする。
キー文字列は SysPropKeys のような定数クラスにまとめ、typo と意味不明なマジックストリングを減らす。
起動時に重要なプロパティをログに出しておくことで、運用・調査がぐっと楽になる。

タイトルとURLをコピーしました