コンストラクターインジェクションと循環依存
テスト容易性のためにコンストラクターインジェクションを優先し、循環依存を検出して@Lazyで解消します。
「コンストラクターインジェクションと循環依存」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
3種類のインジェクション
Springは、コンストラクターインジェクション(推奨)、フィールドインジェクション(フィールドに@Autowiredを付ける方法)、セッターインジェクションをサポートしています。テスト容易性と不変性の観点から、コンストラクターインジェクションが推奨されます。
コンストラクターインジェクション
依存関係をコンストラクターのパラメーターとして宣言します。Springが自動的にインジェクションします。コンストラクターが1つしかない場合、@Autowiredは省略できます(Spring Boot 2.1以降)。
@Service
public class OrderService {
private final OrderRepository repo;
private final EmailService email;
// @Autowired is optional when there is only one constructor
public OrderService(OrderRepository repo, EmailService email) {
this.repo = repo;
this.email = email;
}
}コンストラクターインジェクションが推奨される理由
コンストラクターインジェクションには、次の利点があります。(1) 依存関係をfinalにでき、不変になる、(2) クラスが常に完全に初期化される、(3) 単体テストでSpringに依存せず、new OrderService(mockRepo, mockEmail)を呼び出すだけでよい。
// Unit test — no Spring context needed:
OrderRepository mockRepo = mock(OrderRepository.class);
EmailService mockEmail = mock(EmailService.class);
OrderService service = new OrderService(mockRepo, mockEmail);
// Test service logic directlyフィールドインジェクション(非推奨)
@Autowiredを使用したフィールドインジェクションは簡潔ですが、問題があります。フィールドをfinalにできず、Beanはコンストラクション後に完全に初期化されません。また、単体テストにはSpringまたはMockitoのリフレクションヘルパーが必要になります。
// Avoid in production code:
@Service
public class UserService {
@Autowired // hidden dependency
private UserRepository repo; // cannot be final
}オプションの依存関係に対するセッターインジェクション
適切なデフォルト値を持つオプションの依存関係には、セッターインジェクションを使用します。セッターには@Autowired(required = false)を付けます。
@Service
public class NotificationService {
private SmsSender smsSender; // optional
@Autowired(required = false)
public void setSmsSender(SmsSender s) { this.smsSender = s; }
}循環依存とは
AがBを必要とし、BがAを必要とする場合(直接的または間接的)に、循環依存が発生します。Springは起動時にこれを検出し、BeanCurrentlyInCreationExceptionをスローします。
// CIRCULAR:
@Service class A { A(B b) {} }
@Service class B { B(A a) {} }
// Spring throws: BeanCurrentlyInCreationException: Is there an unresolvable circular reference?循環依存の解消: 3つ目のサービスへのリファクタリング
最も適切な解決策は、共有ロジックを、AとBのどちらからも循環的に依存されない3つ目のサービスへ切り出すことです。
// Before: A <-> B
// After: A -> C, B -> C (no cycle)
@Service class SharedLogic { ... }
@Service class A { A(SharedLogic s) {} }
@Service class B { B(SharedLogic s) {} }循環依存の解消: @Lazy
いずれかのインジェクションポイントに@Lazyを付けます。SpringはBのプロキシをすぐに作成し、実際のBはAが初めてそのメソッドを呼び出したときにだけインスタンス化します。
@Service
public class A {
private final B b;
public A(@Lazy B b) { this.b = b; } // breaks the cycle
}循環依存の解消: ApplicationContextの検索
一方のBeanにApplicationContextをインジェクションし、context.getBean()を使用してもう一方のBeanを遅延的に検索します。ただし、テスト容易性とコードの明確さが低下します。
@Service
public class A implements ApplicationContextAware {
private ApplicationContext ctx;
public void setApplicationContext(ApplicationContext c) { this.ctx = c; }
public void doWork() { ctx.getBean(B.class).help(); }
}循環依存を解消するセッターインジェクション
セッターインジェクションされたBeanは、まず作成されます(コンストラクションが成功します)。その後で依存関係が設定されるため、可変性を代償に循環依存を許容できます。
@Service
public class A {
private B b;
@Autowired public void setB(B b) { this.b = b; } // Spring sets after construction
}spring.main.allow-circular-references(Spring Boot 2.6以降)
Spring Boot 2.6以降では、デフォルトで循環依存が許可されません。リファクタリング中の一時的な対策としてのみ、明示的に有効化してください。spring.main.allow-circular-references=true。
# application.properties (temporary workaround only):
spring.main.allow-circular-references=trueクイックチェック
フィールドインジェクションよりもコンストラクターインジェクションが推奨されるのはなぜですか?
まとめ
不変性、テスト容易性、明示性に優れたコンストラクターインジェクションを推奨します。フィールドインジェクションは避けてください。循環依存を解消するには、@Lazyを使用するか、共有サービスを切り出します。Spring Boot 2.6以降では起動時に循環参照が検出されるため、設計上の警告として扱ってください。
AI チューターと学ぶ Java — 無料
ブラウザでリアルコードを書いて実行し、24/7 の AI チューターから瞬時にサポートを受け、ウェブまたはアプリで続きから学習できます。
- コース
- 104
- レッスン
- 374
よくある質問
「コンストラクターインジェクションと循環依存」レッスンは無料ですか?
はい。「コンストラクターインジェクションと循環依存」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「コンストラクターインジェクションと循環依存」で何を学びますか?
テスト容易性のためにコンストラクターインジェクションを優先し、循環依存を検出して@Lazyで解消します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。
「コンストラクターインジェクションと循環依存」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- 自動構成とSpring Bootスターター
- アプリケーションプロパティとプロファイル
- Beanの配線:@Component、@Service、@Repository
- コンストラクターインジェクションと循環依存