はじめに
Spring Boot を使っていると、
「循環参照エラー」 に出会うことがあります。
- A が B を @Autowired
- B が A を @Autowired
- 起動時に落ちる
- 「Requested bean is currently in creation」と言われる
こういったエラーは、
DI(依存注入)の仕組み を理解すると一気に解決します。
この記事では、循環参照エラーの原因と対処法を
やさしく・実務寄り で整理していきます。
循環参照とは
一言でいうと、
「お互いが依存し合っていて、どちらも先に作れない状態」
です。
例
@Service
public class AService {
@Autowired
private BService b;
}
@Service
public class BService {
@Autowired
private AService a;
}A を作るには B が必要
B を作るには A が必要
→ どちらも作れない
→ 起動エラー
という流れです。
よく出るエラーメッセージ
Requested bean is currently in creation
Error creating bean with name 'aService':
Requested bean is currently in creation: Is there an unresolvable circular reference?
UnsatisfiedDependencyException
Unsatisfied dependency expressed through field 'aService'
どちらも 循環参照が原因 です。
循環参照が起きるパターン(実務で多い順)
Service 同士が相互に依存している
最も多いパターンです。
Controller → Service → Repository → Controller のように戻ってくる
設計の問題。
A → B → C → A のように 3 つ以上で循環
気づきにくいパターン。
コンストラクタ DI で循環
コンストラクタ DI は循環に弱い。
原因を理解する
Spring は Bean を作るときに、
- AService を作ろうとする
- BService が必要
- BService を作ろうとする
- AService が必要
- でも AService はまだ作成中
- → 無限ループになるためエラー
という流れになります。
対処法(全体像)
1. 設計を見直す(最重要)
2. 依存関係を片方向にする
3. 片方を setter / フィールド DI に変える
4. インターフェースを挟む
5. @Lazy を使う(最終手段)
設計を見直す(最も正しい解決法)
循環参照は 設計の匂い(Bad Smell) です。
よくある改善
- AService が BService を呼ぶだけにする
- BService の処理を AService に寄せる
- 共通処理を CService に切り出す
例
A → B → A になっているなら、
A → B に一本化する依存関係を片方向にする
Before
A → B
B → A
After
A → B
片方向にするだけで循環は解消します。
片方を setter / フィールド DI に変える
コンストラクタ DI は循環に弱いですが、 setter DI なら後から注入できるため循環を回避できます。
Before(循環発生)
public AService(BService b) { ... }
public BService(AService a) { ... }After(循環回避)
@Service
public class AService {
private BService b;
@Autowired
public void setB(BService b) {
this.b = b;
}
}※ 設計改善が最優先で、これは応急処置です。
4. インターフェースを挟む
依存関係を抽象化することで循環を解消できます。
Before
AService → BService
BService → AService
After
AService → BInterface
BService → AInterface
@Lazy を使う(最終手段)
どうしても循環を解消できない場合に使います。
@Autowired
@Lazy
private AService a;Spring が遅延ロードするため、 循環が解消されます。
ただし、設計改善が最優先 です。
実務でのチェックリスト
A と B が相互に依存していないか
Service 同士の相互依存は危険。
コンストラクタ DI で循環していないか
setter に変えると解決することも。
共通処理を別クラスに切り出せないか
CService を作ると循環が消える。
@Profile で片方が無効になっていないか
片方だけ有効 → 依存が解決できない。
@Lazy を使う必要が本当にあるか
最終手段。
ログの読み方(おさらい)
循環参照のときは、
ログの最後にこう出ます。
Requested bean is currently in creation
Is there an unresolvable circular reference?
この 2 行が見えたら、
循環参照が原因で確定 です。
まとめ
循環参照エラーは、
「お互いが依存し合っていて、どちらも先に作れない」
という状態です。
- A → B → A の依存関係
- Service 同士の相互依存
- コンストラクタ DI の循環
- @Profile による片方の無効化
難しく聞こえますが、
「依存を片方向にする」
と理解できれば十分です。

循環参照エラーは最初こそ難しく見えますが、
“依存関係の流れ” を意識するだけで
落ち着いて対処できるようになります。
あなたの開発が、今日より少しだけ楽になりますように。

A さんと B さんがお互いに
“あなたが先に…”って譲り合ってる感じだね…

コメント