環境変数取得は「インフラ側の設定をアプリから読む」技
業務システムでは、「本番とステージングで接続先を変えたい」「パスワードや API キーをコードに書きたくない」「コンテナやクラウドの設定をそのまま使いたい」といったニーズが必ず出てきます。
こういう「インフラ側で決める設定」を、アプリケーションから読むための代表的な仕組みが「環境変数」です。
Java では System.getenv(...) で環境変数を取得できますが、そのままベタ書きすると「キー名の typo」「null の扱い」「デフォルト値」「型変換」がバラバラになりがちです。
そこで、「環境変数取得」をユーティリティとしてまとめておくと、実務でかなり扱いやすくなります。
環境変数とシステムプロパティの違いをまず整理する
環境変数は「OS/インフラ側の設定」
環境変数は、OS やコンテナ、クラウドの設定として定義される値です。
Linux なら export APP_ENV=prod、Docker なら -e APP_ENV=prod、Kubernetes なら env: セクションで設定する、あの値です。
Java からはこう取得します。
String env = System.getenv("APP_ENV");
String dbUrl = System.getenv("DB_URL");
Javaここで重要なのは、「アプリケーションの外側で決められる」という点です。
コードを書き換えなくても、デプロイ時の設定だけで挙動を変えられるので、12-factor app 的な設計とも相性が良いです。
システムプロパティとの役割分担
システムプロパティ(System.getProperty("app.env"))は JVM 起動オプションや Java 内部で完結する設定に向いています。
一方、環境変数は OS/コンテナ/クラウド側で管理される設定に向いています。
現場では、例えばこう分けることが多いです。
接続先 URL やパスワード、API キーなど「インフラが管理したいもの」→環境変数。
Java のチューニングやアプリ固有の細かいフラグなど「JVM 起動時に渡したいもの」→システムプロパティ。
どちらを使うかはプロジェクトの方針次第ですが、「環境変数はインフラの領域」という感覚を持っておくと整理しやすくなります。
System.getenv の基本と、そのまま使うとつらいポイント
素朴な取得方法
一番シンプルなコードはこうです。
public class EnvBasic {
public static void main(String[] args) {
String appEnv = System.getenv("APP_ENV");
String dbUrl = System.getenv("DB_URL");
System.out.println("APP_ENV = " + appEnv);
System.out.println("DB_URL = " + dbUrl);
}
}
Javaキーが存在しない場合、System.getenv は null を返します。
ここで初心者がよくハマるのが、「環境変数が設定されていないのに、そのまま使ってしまって NullPointerException」パターンです。
また、環境変数はすべて文字列なので、「真偽値」「数値」として使いたい場合は自分でパースする必要があります。
これを呼び出し側で毎回やっていると、コードが散らかり、挙動もバラバラになります。
実務で使える「環境変数取得ユーティリティ」の最小形
null とデフォルト値、型変換を一箇所に集約する
まずは、よく使うパターンをユーティリティに閉じ込めます。
public final class Envs {
private Envs() {}
public static String get(String key) {
return System.getenv(key);
}
public static String getOrDefault(String key, String defaultValue) {
String value = System.getenv(key);
return (value != null) ? value : defaultValue;
}
public static boolean getBoolean(String key, boolean defaultValue) {
String value = System.getenv(key);
if (value == null) {
return defaultValue;
}
return Boolean.parseBoolean(value);
}
public static int getInt(String key, int defaultValue) {
String value = System.getenv(key);
if (value == null) {
return defaultValue;
}
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultValue;
}
}
}
Java使う側はこう書けます。
String appEnv = Envs.getOrDefault("APP_ENV", "dev");
boolean featureX = Envs.getBoolean("FEATURE_X_ENABLED", false);
int maxConnections = Envs.getInt("DB_MAX_CONNECTIONS", 10);
Javaここで深掘りしたいポイントは三つです。
一つ目は、「デフォルト値の扱いをユーティリティ側に寄せている」ことです。
「設定がなければ dev」「設定がなければ false」「設定がなければ 10」といったポリシーを、呼び出し側ではなくメソッド呼び出しで明示できます。
二つ目は、「文字列→boolean/int の変換を一箇所に集約している」ことです。Boolean.parseBoolean や Integer.parseInt をあちこちに書くより、getBoolean/getInt に閉じ込めたほうが読みやすく、バグも減ります。
三つ目は、「パース失敗時の扱い(NumberFormatException のときはデフォルト値に戻す)」を統一していることです。
ここを「例外を投げる」にするか「デフォルトに戻す」にするかも、ユーティリティ側で方針を決めておくと、アプリ全体で一貫性が出ます。
「必須環境変数」と「任意環境変数」を分けて扱う
必須環境変数は「なければ起動時に落とす」
例えば、「DB_URL が設定されていない状態でアプリが動いても意味がない」といったケースがあります。
こういう「必須環境変数」は、起動時にチェックして、なければ即エラーにしたほうが健全です。
public static String getRequired(String key) {
String value = System.getenv(key);
if (value == null || value.isBlank()) {
throw new IllegalStateException("Required environment variable is missing: " + key);
}
return value;
}
Java使う側はこうです。
String dbUrl = Envs.getRequired("DB_URL");
Javaここでの重要ポイントは、「必須かどうかを if 文ではなくメソッド名で表現している」ことです。getRequired と書かれていれば、「ここで設定がなければ起動時に落ちる」という意図が一目で分かります。
任意環境変数は「デフォルト付き」で扱う
一方、「あれば使うけど、なければデフォルトでいい」という任意環境変数は、getOrDefault/getBoolean/getInt で十分です。
この「必須/任意」の切り分けを、ユーティリティのメソッドレベルで表現しておくと、設定の意味がコードから読み取りやすくなります。
よく使う環境変数キーを定数としてまとめる
マジックストリングを減らす
"APP_ENV" や "DB_URL" のようなキー文字列を、コードのあちこちにベタ書きすると、typo や変更漏れの原因になります。
そこで、「環境変数のキーを集約したクラス」を作るのが定番です。
public final class EnvKeys {
private EnvKeys() {}
public static final String APP_ENV = "APP_ENV";
public static final String DB_URL = "DB_URL";
public static final String DB_MAX_CONNECTIONS = "DB_MAX_CONNECTIONS";
public static final String FEATURE_X_ENABLED = "FEATURE_X_ENABLED";
}
Java使う側はこうなります。
String env = Envs.getOrDefault(EnvKeys.APP_ENV, "dev");
String dbUrl = Envs.getRequired(EnvKeys.DB_URL);
int maxConnections = Envs.getInt(EnvKeys.DB_MAX_CONNECTIONS, 10);
Javaここで深掘りしたいのは、「キー名を“意味のある定数名”にすることで、設定の意図がコードから伝わる」ことです。"DB_MAX_CONNECTIONS" だけ見ても何のことか分かりにくいですが、EnvKeys.DB_MAX_CONNECTIONS なら「DB の最大接続数だな」とすぐに分かります。
起動時に環境変数をログに出しておく実務テク
「どんな設定で動いていたか」を後から追えるようにする
本番でトラブルが起きたとき、「このプロセスはどんな環境変数で動いていたのか」が分からないと、原因調査が難しくなります。
そこで、アプリ起動時に「重要な環境変数」をまとめてログに出しておくのは、実務でよく効くテクニックです。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class EnvLogger {
private static final Logger log = LoggerFactory.getLogger(EnvLogger.class);
private EnvLogger() {}
public static void logStartupEnv() {
log.info("Environment at startup: APP_ENV={}, DB_URL={}, DB_MAX_CONNECTIONS={}, FEATURE_X_ENABLED={}",
Envs.getOrDefault(EnvKeys.APP_ENV, "dev"),
Envs.getOrDefault(EnvKeys.DB_URL, "(not set)"),
Envs.getInt(EnvKeys.DB_MAX_CONNECTIONS, 10),
Envs.getBoolean(EnvKeys.FEATURE_X_ENABLED, false)
);
}
}
Java起動時に一度だけこう呼びます。
EnvLogger.logStartupEnv();
Javaこれで、「どの環境で」「どんな設定値で」動いていたかがログから追えるようになります。
ここでのポイントは、「設定値を“使うときだけ”でなく、“起動時にまとめて記録しておく”」という発想です。
セキュリティと環境変数:パスワードをどう扱うか
環境変数には機密情報も載る
DB パスワードや API キー、トークンなどを環境変数で渡すことはよくあります。
これは「コードや設定ファイルに平文で書かない」という意味では安全側の選択ですが、
それをそのままログに出したり、画面に表示したりすると、情報漏えいのリスクになります。
ユーティリティ側で「マスクする」仕組みを用意しておくと安心です。
public static String masked(String value) {
if (value == null || value.isEmpty()) {
return "(empty)";
}
return "****";
}
Javaログ出力時にこう使います。
String dbPassword = System.getenv("DB_PASSWORD");
log.info("DB_PASSWORD={}", Envs.masked(dbPassword));
Java「どの環境変数はログに出してよいか」「どれはマスクすべきか」は、チームでルールを決めておくと安全です。
テストコードで環境変数をどう扱うか
Java から環境変数は基本的に書き換えられない
System.setProperty でシステムプロパティは上書きできますが、環境変数は Java から基本的に変更できません。
そのため、テストで環境変数に依存するコードを書くときは、設計を工夫する必要があります。
一つの考え方は、「環境変数取得ユーティリティをインターフェース化して、テスト時に差し替える」ことです。
public interface EnvProvider {
String get(String key);
}
public class SystemEnvProvider implements EnvProvider {
@Override
public String get(String key) {
return System.getenv(key);
}
}
Javaアプリ側では EnvProvider をコンストラクタで受け取り、本番では SystemEnvProvider、テストでは「テスト用の実装(任意の値を返す)」を渡します。
こうしておくと、「環境変数に依存するロジック」をテストしやすくなります。
まとめ:環境変数取得ユーティリティで身につけるべき感覚
環境変数取得は、「ただ文字列を読む」だけではなく、「インフラ側の設定をどう設計し、どう安全にコードから扱うか」という話です。
押さえておきたいポイントは次の通りです。
System.getenv をそのままばらまかず、Envs のようなユーティリティに閉じ込める。
文字列だけでなく、boolean/int などへの変換と、パース失敗時の扱いを一箇所で決める。
「必須環境変数」と「任意環境変数」をメソッドレベルで分け、設定漏れは起動時に気づけるようにする。
キー文字列は EnvKeys のような定数クラスにまとめ、typo と意味不明なマジックストリングを減らす。
起動時に重要な環境変数をログに出しておきつつ、パスワードなど機密情報はマスクする。
