- Spring
Spring Boot循環参照の直し方|form a cycleの原因と対処
「The dependencies of some of the beans in the application context form a cycle」は、Bean(Spring が管理するオブジェクト)同士が、お互いを必要とする輪になっているときの起動エラーです。 A が B を必要とし、B が A を必要とすると、どちらも先に作れません。Spring Boot 2.6 からは、この輪(循環参照)が既定で禁止になりました。
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| orderService defined in file [/app/classes/com/example/demo/order/OrderService.class]
↑ ↓
| customerService defined in file [/app/classes/com/example/demo/customer/CustomerService.class]
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
直し方の基本は、輪の中の依存を1本切ることです。 方法ごとの違いは次のとおりです。
| 直し方 | やること | 向いている場面 |
|---|---|---|
| 責務を分ける | 両方が使う処理を、第3のクラスに移す | 多くの場合の第一候補 |
| 設定クラスを分ける | @Bean メソッドを別の @Configuration に移す |
設定クラスが輪に入っているとき |
| イベントにする | 直接呼ばず、ApplicationEvent で知らせる |
「終わったら知らせる」だけの依存 |
| ObjectProvider / @Lazy | 使う瞬間まで取得を遅らせる | すぐに構造を変えられないとき |
allow-circular-references: true |
Spring に自動で解かせる | 最後の手段(Action の文もそう書いている) |
確認環境:Spring Boot 4.1.1(Spring Framework 7.0.9)。エラーの文面と図の描き方は、GitHub の v4.1.1 タグの BeanCurrentlyInCreationFailureAnalyzer のソースとテストで確かめ、図の描画処理を同じ書式で OpenJDK 21.0.10(Linux)で動かして出力を得ました。2.6 での変更は Spring Boot 2.6 のリリースノート、注入の仕組みは Spring Framework のリファレンスとソースで確かめました(確認日 2026年10月12日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Boot循環参照の直し方|form a cycleの原因と対処」
Spring Boot 2.6 で既定が「禁止」に変わった
Spring Boot 2.6 のリリースノートには、次のように書かれています。
- Bean 間の循環参照は、既定で禁止になった
BeanCurrentlyInCreationExceptionで起動しない場合は、設定を変えて輪を切ることを強く勧める- できない場合は
spring.main.allow-circular-referencesをtrueにすると、2.5 の動き(自動で輪を解こうとする)に戻る
| 版 | 循環参照の扱い |
|---|---|
| Spring Framework 単体 | 既定で許可(allowCircularReferences の初期値は true) |
| Spring Boot 2.5 まで | 許可。フィールド注入・セッター注入の輪は自動で解かれた |
| Spring Boot 2.6 以降(4.1.1 も同じ) | 既定で禁止。spring.main.allow-circular-references の既定値は false |
Boot 2.5 以前から上げたアプリで、急にこのエラーが出るのはこのためです。 コードは変えていなくても、Boot の既定が変わったことで起動しなくなります。Boot 4.1.1 の設定メタデータでも、spring.main.allow-circular-references の既定値は false でした。
エラーの図の読み方
図は、Spring Boot の FailureAnalyzer(起動失敗の説明文を作る部品)が描いています。ソースの描き方を整理すると次のとおりです。
| 記号 | 意味 |
|---|---|
┌─────┐ |
輪の始まり |
| 〈Bean名〉 defined in 〈場所〉 |
輪に入っている Bean |
↑ ↓ |
上の Bean が下の Bean を必要とする(↓)。最後から最初へ戻る(↑) |
└─────┘ |
輪の終わり(一番下の Bean が、一番上の Bean を必要とする) |
┌──->──┐ と └──<-──┘ |
Bean が自分自身を必要としている(1つだけの輪) |
枠の外(左に | が無い行) |
輪の外から、輪に入るきっかけになった Bean |
読み方は「上から下へ、矢印の向きに『必要とする』」です。 冒頭の例なら、orderService が customerService を必要とし、customerService が orderService を必要としています。
枠の外に Bean が出る場合
輪の外の Bean が、輪の中の Bean を最初に作ろうとした場合は、枠の上に出ます。同じ描画処理を Java 21 で動かした出力です。
The dependencies of some of the beans in the application context form a cycle:
orderController defined in file [/app/classes/com/example/demo/order/OrderController.class]
┌─────┐
| orderService defined in file [/app/classes/com/example/demo/order/OrderService.class]
↑ ↓
| customerService defined in file [/app/classes/com/example/demo/customer/CustomerService.class]
↑ ↓
| pointService defined in file [/app/classes/com/example/demo/point/PointService.class]
└─────┘
orderController は輪の外なので、直す対象ではありません。 輪は orderService → customerService → pointService → orderService の3つです。この3本の矢印のうち1本を切れば起動します。
「defined in」の後ろの場所
| 表示 | Bean の作られ方 |
|---|---|
defined in file [… /OrderService.class] |
@Service などを付けたクラス |
defined in class path resource [com/example/demo/config/SecurityConfig.class] |
設定クラスの @Bean メソッド |
(field private … ) |
場所の情報が無い Bean。注入先のフィールドが出る |
class path resource […Config.class] が出たら、設定クラスの @Bean メソッドが輪に入っています。 後で説明する「設定クラスを分ける」が効く形です。
例外の原文
図の上のスタックトレースには、Spring Framework の例外が出ます。ソースでの文面は次のとおりです。
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'orderService': Requested bean is currently in creation: Is there an unresolvable circular reference or an asynchronous initialization dependency?
「Requested bean is currently in creation(求められた Bean は、まだ作っている途中)」が、輪になっているという意味です。
なぜコンストラクタ注入で起きるのか
コンストラクタ注入では、相手が完成していないと自分を new できません。 A のコンストラクタに B が要り、B のコンストラクタに A が要ると、どちらから作っても止まります。
@Service
public class OrderService {
private final CustomerService customerService;
public OrderService(CustomerService customerService) { // ← CustomerService が先に要る
this.customerService = customerService;
}
}
@Service
public class CustomerService {
private final OrderService orderService;
public CustomerService(OrderService orderService) { // ← OrderService が先に要る
this.orderService = orderService;
}
}
Spring Framework のリファレンスでも、主にコンストラクタ注入を使うと「解決できない循環依存」になりうること、そのとき BeanCurrentlyInCreationException が出ることが書かれています。
| 注入の方法 | Boot 2.5 まで | Boot 2.6 以降の既定 | allow-circular-references: true |
|---|---|---|---|
| コンストラクタ注入 | エラー | エラー | エラー(解けない) |
フィールド注入(@Autowired) |
自動で解決 | エラー | 自動で解決 |
| セッター注入 | 自動で解決 | エラー | 自動で解決 |
フィールド・セッター注入は、「中身がまだ空の A を先に B へ渡し、あとで A を完成させる」ことで輪を解けます(Framework のソースでは、Bean を作り始めた直後の参照を先に公開するしくみ)。Boot 2.6 以降は、このしくみを既定で止めています。
コンストラクタ注入のまま allow-circular-references: true にしても直りません。 そのときの Action はソース上、次の文に変わります。
Despite circular references being allowed, the dependency cycle between beans could not be broken. Update your application to remove the dependency cycle.
輪がエラーになるのは、コンストラクタ注入の欠点ではありません。 起動時に設計の輪が見つかるので、早く気づけます。注入方法の違いは、関連記事「Spring DIとは|@Autowiredとコンストラクタインジェクション」で扱っています。
直し方1:責務を分ける(第一候補)
輪になるのは、2つのクラスがお互いの一部の処理を使っているからです。 両方が使う処理を第3のクラスに移すと、矢印が一方向になります。
直す前の依存は次の形です。
OrderServiceは、注文を作るときにCustomerService.findRank()(会員ランク)を使うCustomerServiceは、退会のときにOrderService.countOpenOrders()(未完了の注文数)を使う
// 1. 両方から使われる「読むだけ」の処理を、新しいクラスへ移す
@Service
public class OrderQueryService {
private final OrderRepository orderRepository;
public OrderQueryService(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
public long countOpenOrders(long customerId) {
return orderRepository.countByCustomerIdAndStatus(customerId, OrderStatus.OPEN);
}
}
// 2. CustomerService は OrderService ではなく OrderQueryService を使う
@Service
public class CustomerService {
private final CustomerRepository customerRepository;
private final OrderQueryService orderQueryService;
public CustomerService(CustomerRepository customerRepository, OrderQueryService orderQueryService) {
this.customerRepository = customerRepository;
this.orderQueryService = orderQueryService;
}
public Rank findRank(long customerId) { … }
public void withdraw(long customerId) {
if (orderQueryService.countOpenOrders(customerId) > 0) {
throw new IllegalStateException("未完了の注文があります");
}
…
}
}
// 3. OrderService は今までどおり CustomerService を使う
@Service
public class OrderService {
private final CustomerService customerService;
private final OrderRepository orderRepository;
public OrderService(CustomerService customerService, OrderRepository orderRepository) {
this.customerService = customerService;
this.orderRepository = orderRepository;
}
}
依存は OrderService → CustomerService → OrderQueryService → OrderRepository の一方向になり、輪が消えます。
同じ層のサービス同士が呼び合う形は、輪ができやすい形です。 「上の層は下の層だけを呼ぶ」「読むだけの処理は別クラス」と決めておくと、増えにくくなります。
直し方2:設定クラスを分ける(@Bean が輪に入るとき)
設定クラスの @Bean メソッドは、その設定クラス自体が作られてから呼ばれます。 そのため、設定クラスがほかの Bean を受け取り、その Bean が同じ設定クラスの @Bean を必要とすると、輪になります。
Spring Security の設定でよく見る形です。
@Configuration
public class SecurityConfig {
private final UserService userService; // ← UserService が要る
public SecurityConfig(UserService userService) {
this.userService = userService;
}
@Bean
PasswordEncoder passwordEncoder() { // ← UserService はこれが要る
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
@Service
public class UserService {
public UserService(UserRepository repo, PasswordEncoder passwordEncoder) { … }
}
securityConfig → userService → passwordEncoder(securityConfig の中)→ securityConfig の輪です。直し方は3つあります。
// A. PasswordEncoder を別の設定クラスへ移す(分かりやすい)
@Configuration
public class PasswordConfig {
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
// B. @Bean メソッドを static にする(設定クラスを作らずに呼べる)
@Bean
static PasswordEncoder passwordEncoder() { … }
// C. 設定クラスのフィールドで受け取らず、@Bean メソッドの引数で受け取る
@Bean
SecurityFilterChain filterChain(HttpSecurity http, UserService userService) throws Exception { … }
A が第一候補です。 「設定クラスはほかの Bean をフィールドで持たない。要るものは @Bean メソッドの引数で受け取る」と決めておくと、この形の輪は起きにくくなります。Security の設定全体は、関連記事「Spring Security入門|SecurityFilterChainとDB認証」で扱っています。
直し方3:ApplicationEvent で知らせる
「A の処理が終わったら B に知らせる」だけの依存は、イベントに置き換えると矢印が消えます。 A はイベントを出すだけで、B を知りません。
// イベント(ただのデータ)
public record OrderPlacedEvent(long orderId, long customerId) {}
// 出す側:CustomerService を受け取らない
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
public OrderService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Transactional
public void place(Order order) {
…
publisher.publishEvent(new OrderPlacedEvent(order.getId(), order.getCustomerId()));
}
}
// 受け取る側
@Service
public class CustomerService {
private final OrderService orderService; // こちら向きの矢印は残ってよい
public CustomerService(OrderService orderService) {
this.orderService = orderService;
}
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
// ポイント加算など
}
}
| 受け取り方 | 呼ばれるタイミング |
|---|---|
@EventListener |
publishEvent の中で、すぐ(同じトランザクションの中) |
@TransactionalEventListener |
既定ではコミットの後(AFTER_COMMIT) |
戻り値が要る処理(ランクを聞く、など)はイベントに向きません。 その場合は直し方1の責務分割を使います。
直し方4:ObjectProvider・@Lazy で取得を遅らせる
起動時ではなく、使う瞬間に相手を取りにいけば、起動時の輪は起きません。 構造をすぐに変えられないときの手段です。
// ObjectProvider:メソッドを呼んだときに取得する
@Service
public class CustomerService {
private final ObjectProvider<OrderService> orderService;
public CustomerService(ObjectProvider<OrderService> orderService) {
this.orderService = orderService;
}
public void withdraw(long customerId) {
long open = orderService.getObject().countOpenOrders(customerId);
…
}
}
// @Lazy:代わりのオブジェクト(プロキシ)を先に渡し、初めて使うときに本物を取得する
@Service
public class CustomerService {
private final OrderService orderService;
public CustomerService(@Lazy OrderService orderService) {
this.orderService = orderService;
}
}
| ObjectProvider | @Lazy | |
|---|---|---|
| コードの見た目 | getObject() が増える |
普通の型のまま |
| 遅らせていることが分かるか | 型で分かる | 注釈を見ないと分からない |
| 既定の禁止設定のままで | 起動する | 起動する |
| 注意 | 輪自体は残る | 輪自体は残る。final クラスにはプロキシを作れない |
どちらも、起動時の輪を消すだけで、設計上の輪は残ります。 一時しのぎとして使い、後で直し方1・2に置き換える前提にすると、輪が増えるのを防げます。
spring.main.allow-circular-references の位置づけ
spring:
main:
allow-circular-references: true
| 観点 | 内容 |
|---|---|
| 効果 | Boot 2.5 以前と同じく、フィールド・セッター注入の輪を自動で解く |
| 効かない場合 | コンストラクタ注入の輪 |
| 公式の位置づけ | Action の文では「As a last resort(最後の手段)」 |
| 使う場面の例 | Boot の版上げを先に終わらせ、輪の解消は別の作業に分けたいとき |
自動で解いた輪では、相手がまだ「作りかけ」の状態で渡されます。 その Bean が後からプロキシ(代理オブジェクト)で包まれると、ソース上、次の別のエラーになることがあります。
Bean with name 'orderService' has been injected into other beans [customerService] in its raw version as part of a circular reference, but has eventually been wrapped. This means that said other beans do not use the final version of the bean.
使う場合は、application.yml にコメントで理由と解消の予定を書いておくと、そのまま残り続けるのを防げます。設定キーの一覧は関連記事「Spring Boot application.properties設定一覧」で扱っています。
直し方の選び方
- 図の枠の中の Bean(輪)を全部書き出す
class path resourceがあれば → 設定クラスを分ける- 片方が「知らせるだけ」なら → イベント
- お互いの処理の一部を使っているなら → 責務を分ける
- 今すぐ構造を変えられないなら → ObjectProvider(後で直す前提)
- 版上げのために一時的に動かしたいだけなら →
allow-circular-references(最後の手段)
よくある質問
Q. フィールド注入(@Autowired)に戻せば直りますか。
A. Boot 2.6 以降の既定では直りません。フィールド注入の輪も禁止されているためです。allow-circular-references: true と組み合わせると起動しますが、最後の手段の扱いです。
Q. 輪が3つ以上の Bean でできていて、どこを切ればよいか分かりません。 A. 図の矢印のうち、「その処理を本当にそのクラスが持つべきか」が一番あやしい1本を選びます。多くの場合、下の層(リポジトリ寄り)から上の層(サービス・コントローラー寄り)へ向かう矢印が、切る候補になります。
Q. テストのときだけ循環参照エラーが出ます。
A. テスト用の設定クラスや @MockitoBean の差し替えで、本番と違う依存が入っていないかを確かめます。テスト専用の @TestConfiguration が本番の Bean を受け取っている形は、輪になりやすい形です。
Q. 自分自身を参照する輪(┌──->──┐)が出ました。
A. Bean が自分の型を注入しようとしています。同じクラスのメソッドを @Transactional 付きで呼ぶために自分を注入した場合に起きやすい形です。処理を別クラスに分けると解消します。関連記事「Spring @Transactionalが効かない原因8選|ロールバックの直し方」で扱っています。
まとめ
- Bean 同士がお互いを必要とする輪があると出る起動エラー。中身は
BeanCurrentlyInCreationException - Spring Boot 2.6 から既定で禁止。
spring.main.allow-circular-referencesの既定値はfalse - 図は枠の中が輪、上から下へ「必要とする」。枠の外は直す対象ではない
- コンストラクタ注入の輪は、許可しても解けない
- 直し方は責務分割 → 設定クラス分離 → イベント → ObjectProvider/@Lazy の順で検討
allow-circular-references: trueは最後の手段
参考・出典
確認日はいずれも 2026年10月12日です。
- Spring Boot 2.6 Release Notes「Circular References Prohibited by Default」:https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.6-Release-Notes
- Spring Framework リファレンス「Dependency Injection」(Circular dependencies):https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html
- Spring Boot ソースコード(v4.1.1)BeanCurrentlyInCreationFailureAnalyzer:https://github.com/spring-projects/spring-boot/blob/v4.1.1/core/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/BeanCurrentlyInCreationFailureAnalyzer.java
- Spring Framework ソースコード(v7.0.9)AbstractAutowireCapableBeanFactory:https://github.com/spring-projects/spring-framework/blob/v7.0.9/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java
- Spring Boot リファレンス「Common Application Properties」:https://docs.spring.io/spring-boot/appendix/application-properties/index.html
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「Spring Boot循環参照の直し方|form a cycleの原因と対処」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。