これは「break を使ったループ制御+安全設計」を実務テンプレート化したものです。
Java+SLF4J/Spring Boot想定で、そのまま業務コードに組み込みやすい形にしてあります。
すべて 例外・ログ・上限制御・後処理 を備えています。
1. 基本構造(安全リトライの考え方)
┌──────────────┐
│ try 処理 │
│ ├ 成功 → break │
│ └ 失敗 → ログ │
│ + 待機 │
│(上限まで繰返) │
└──────────────┘
↓
終了処理・通知
特徴
✅ 無限ループしない(上限付き)
✅ ログに「何回目の失敗か」を残す
✅ 例外を握りつぶさず、最後に再スロー可能
✅ Thread.sleep で簡易バックオフ対応
テンプレート①:基本リトライ(IOException想定)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class RetryExample {
private static final Logger log = LoggerFactory.getLogger(RetryExample.class);
private static final int MAX_RETRY = 3;
public static void main(String[] args) {
boolean success = false;
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
try {
log.info("API呼び出し試行 {} 回目", attempt);
callApi(); // 仮の外部処理
log.info("処理成功");
success = true;
break; // 成功したら終了
} catch (IOException e) {
log.warn("試行 {} 回目で失敗: {}", attempt, e.getMessage());
if (attempt == MAX_RETRY) {
log.error("最大試行回数に達しました。処理を中断します。", e);
} else {
try {
Thread.sleep(1000L * attempt); // バックオフ(1s,2s,3s)
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("スリープ中断", ie);
break;
}
}
}
}
if (!success) {
log.error("すべての試行に失敗しました。");
}
}
private static void callApi() throws IOException {
// 実際には外部API呼び出しなど
if (Math.random() < 0.7) { // 70%の確率で失敗
throw new IOException("一時的な通信エラー");
}
}
}
Java🧭 ポイント解説
break:成功時にループを抜けるMAX_RETRY:無限リトライ防止Thread.sleep():簡易的なバックオフ制御- ログレベル:
info:開始・成功warn:再試行中error:全失敗
テンプレート②:メソッド分割+再スロー(実務クラス構成)
public class ApiService {
private static final Logger log = LoggerFactory.getLogger(ApiService.class);
private static final int MAX_RETRY = 5;
public void executeWithRetry() throws IOException {
for (int attempt = 1; attempt <= MAX_RETRY; attempt++) {
try {
callExternalApi();
log.info("外部API呼び出し成功");
break;
} catch (IOException e) {
log.warn("試行 {} 回目で失敗: {}", attempt, e.getMessage());
if (attempt == MAX_RETRY) {
log.error("最大リトライ回数に達しました。例外を再スローします。", e);
throw e; // 呼び出し元でハンドリング
}
waitBeforeRetry(attempt);
}
}
}
private void callExternalApi() throws IOException {
if (Math.random() < 0.5) throw new IOException("通信エラー");
}
private void waitBeforeRetry(int attempt) {
try {
Thread.sleep(1000L * attempt);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("スリープ中断", ie);
}
}
}
Java💡 現場でのポイント
- 実務ではメソッドを分割して「責務」を明確にする。
throw e;で上位層(Service / Controller)に例外を伝播。- テスト時は
Math.random()をモックに置き換える。
テンプレート③:Spring Boot + @Retryable(ライブラリ利用)
Spring Bootであれば、spring-retry を利用することで安全なリトライを宣言的に書けます。
内部的には break と同様の制御を自動で行います。
import org.springframework.retry.annotation.Retryable;
import org.springframework.retry.annotation.Backoff;
import org.springframework.stereotype.Service;
import java.io.IOException;
@Service
public class RetryableService {
@Retryable(
value = IOException.class,
maxAttempts = 3,
backoff = @Backoff(delay = 1000, multiplier = 2)
)
public void call() throws IOException {
if (Math.random() < 0.8) {
throw new IOException("APIエラー");
}
System.out.println("成功しました");
}
}
Java🧩 特徴
maxAttempts:リトライ上限@Backoff(delay, multiplier):指数バックオフ- 明示的なループ不要(内部で break 相当の制御)
- 実務では「Spring + SLF4J」構成が多い
テンプレート④:Result型+ループ終了判定(関数型風)
副作用を減らしたい場合、結果を Result オブジェクトで包んで戻す方法もあります。
public class SafeRetry {
public record Result(boolean success, String message) {}
public Result doRetryableJob() {
for (int attempt = 1; attempt <= 3; attempt++) {
try {
perform();
return new Result(true, "成功 " + attempt + "回目");
} catch (Exception e) {
if (attempt == 3) {
return new Result(false, "失敗: " + e.getMessage());
}
}
}
return new Result(false, "不明な終了");
}
private void perform() throws Exception {
if (Math.random() < 0.8) throw new Exception("一時エラー");
}
}
Javaテンプレート⑤:リトライ付きDB接続例(擬似)
Connection conn = null;
for (int attempt = 1; attempt <= 3; attempt++) {
try {
conn = DriverManager.getConnection(url, user, pass);
System.out.println("DB接続成功");
break;
} catch (SQLException e) {
System.err.println("接続失敗 " + attempt + "回目: " + e.getMessage());
if (attempt == 3) throw e;
Thread.sleep(500 * attempt);
}
}
Java実務の安全設計ポイントまとめ
| 項目 | ベストプラクティス |
|---|---|
| 🔁 リトライ回数 | 固定上限を必ず設ける(無限ループ禁止) |
| ⏱ バックオフ | Thread.sleep() や @Backoff で段階的遅延 |
| 🪵 ロギング | 成功・失敗・最終失敗をすべて残す |
| 🧨 例外 | 必要に応じて上位層に再スロー |
| 🧹 後処理 | finally または try-with-resources でリソース解放 |
| 🔒 並列実行 | スレッドセーフなAPIで書く(synchronizedやExecutor管理) |
✅ まとめ
break文+上限+例外+ログを組み合わせれば、
実務でも安全に「失敗に強い」ループ処理が書ける。


