「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設定一覧」で扱っています。

直し方の選び方

  1. 図の枠の中の Bean(輪)を全部書き出す
  2. class path resource があれば → 設定クラスを分ける
  3. 片方が「知らせるだけ」なら → イベント
  4. お互いの処理の一部を使っているなら → 責務を分ける
  5. 今すぐ構造を変えられないなら → ObjectProvider(後で直す前提)
  6. 版上げのために一時的に動かしたいだけなら → 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期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?