Springの構造関数注入のサイクル依存問題
26680 ワード
一、循環依存springのループ依存とは、主に2つのクラスが互いに@Autowiredによって自動的に依存して相手に注入されることを意味する.すなわち、クラスAは1つのクラスBのオブジェクト参照を含み、自動的に注入する必要があり、クラスBは1つのクラスAを含むオブジェクト参照も自動的に注入する必要がある. サイクル依存問題に対してspringは注入方式によって異なる処理戦略を採用し、双方が属性値注入またはsetter法注入を使用している場合、springは自動的にサイクル依存注入問題を解決することができ、アプリケーションは成功に起動することができる.両方ともコンストラクション関数を使用して相手またはメインbeanオブジェクト(Spring起動中に先にロードされたbeanオブジェクト)にコンストラクション関数を使用して注入する場合、springはループ依存注入を解決できず、プログラムエラーで起動できません.
二、解決できる循環依存
サイクル依存の状況を解決できる主beanは、依存するbeanを属性またはsetter法によって注入し、構造関数によって注入しない.
サイクル依存解決の実現
実装ベースループ依存性の解決:singletonObjects,earlySingletonObjects,singletonFactoriesの3段階キャッシュ実装.以下は、主にAbstractBeanFactoryのdoGetBeanメソッドで呼び出されるDefaultSingletonBeanRegistryのgetSingletonメソッドの実装です.
インプリメンテーションプロセスマスターbeanオブジェクトの作成時にAbstractBeanFactoryがD e f a u l tSingletonBeanRegistryのgetSingletonメソッドを呼び出す:マスターbeanをsingletonsCurrentlyInCreationリストに入れることで、以上の3段階キャッシュ実装メソッドgetSingletonが、2、3段階の保存earlySingletonObjectsに入ることができ、singletonFactoriesがこのマスターbeanを検索し、主に依存beanにプライマリbeanを検索するときに使用されます. A b s t r a c t o w i r e CapableBeanFactoryのcreateBeanメソッドが呼び出され、createBeanメソッドはAbstractAutowireCapableBeanFactoryのdoCreateBeanメソッドが呼び出されます.doCreateBeanメソッドは、addSingletonFactoryメソッドを先に呼び出し、三次キャッシュsingletonFactoriesにマスターbeanを入れるタイプのObjectFactoryのsingletonFactoryオブジェクト作成ファクトリでは、依存するbeanオブジェクトを作成するときに、マスターbeanオブジェクト参照を三次キャッシュメカニズムで取得できます. doCreateBeanメソッドでは、populateBeanを呼び出して属性付与を行い、ステップ2のaddSingletonFactoryメソッドはpopulateBeanメソッドを呼び出す前に呼び出されるので、populateBeanを呼び出してメインbeanオブジェクトに属性値注入またはsetter注入を行うと、メインbeanの作成ファクトリはsingletonFactoriesキャッシュに既に存在する. populateBeanメソッドで依存するbeanオブジェクトを検索または作成します.依存するbeanオブジェクトを作成すると、AbstractBeanFactoryのdoGetBeanメソッドが呼び出されます.ループ依存性がある場合、依存beanは属性注入、setter法注入、または構造関数注入によって主beanを注入することができる. 属性値またはsetterメソッドで注入された場合、ループ依存の問題はありません.プライマリbeanと依存するbeanは、コンストラクション関数を呼び出してオブジェクトを作成することに成功し、オブジェクト参照によって相手を参照することができます.属性値注入とsetterメソッド注入は、beanオブジェクトの後置プロセッサBeanPostProcessorによってそのbeanオブジェクトの属性値を注入する. コンストラクション関数を介して依存beanオブジェクトがプライマリbeanオブジェクトに注入された場合、コンストラクション関数を呼び出して依存beanオブジェクトを作成し、プライマリbeanオブジェクトを検索する必要がある場合、検索時にAbstractBeanFactoryのdoGetBeanメソッドを呼び出してプライマリbeanオブジェクトを取得し続け、一方、AbstractBeanFactoryのdoGetBeanメソッド呼び出しgetSingletonメソッドは、3つのキャッシュがプライマリbeanオブジェクトを得ることができるかどうかをチェックします. ステップ1およびステップ2から分かるように、getSingletonメソッドのsingletonsCurrentlyInCreationおよびsingletonFactoriesにはすでにマスターbeanに関する情報があるため、マスターbeanオブジェクトは、マスターbeanオブジェクトの作成工場singletonFactoriesによって事前に露出したマスターbeanオブジェクトを作成し、セカンダリーキャッシュearlySingletonObjectsを入れることによって検索され、したがって、この依存beanのプライマリbeanへの依存注入を解決することができ、依存オブジェクトbeanの作成に成功する.
この依存beanオブジェクトが作成されると、プライマリbeanのプロパティは完全に注入に成功する.一方、依存beanはプライマリbean内のオブジェクト参照を含むため、依存beanオブジェクトはプライマリbeanにアクセスして使用することができ、依存beanオブジェクトも完全なオブジェクトであり、最終的にプライマリbeanと依存beanとの間の循環依存問題を解決する. 最後に、注入に成功すると、1つのbeanオブジェクトが1つのキャッシュsingletonObjectsに格納され、事前に露出したオブジェクトが格納されている2つのキャッシュearlySingletonObjectsとそのbeanオブジェクトの作成ファクトリキャッシュearlySingletonObjectsが除去されます.D e f a ultSingletonBeanRegistryのgetSingletonメソッド実装とaddSingletonメソッド実装は、次のようになります: 三、解決できない循環依存問題主beanに依存するbeanをコンストラクション関数によって注入する. 以下controllerを主beanとし、サービスは依存するbean: 依存するbeanがプライマリbeanに属性値またはsetter法によって注入され、依存するbeanがコンストラクション関数を使用してプライマリbeanオブジェクトに注入される場合、ループ依存の問題は発生しません.
四、まとめループ依存が存在する場合、主beanオブジェクトは構造関数によって依存するbeanオブジェクトを注入することができず、依存するbeanオブジェクトは制限されず、すなわち、3つの注入方式のいずれかによって主beanオブジェクトを注入することができる. 主beanオブジェクトがコンストラクション関数で依存するbeanオブジェクトを注入すると、依存するbeanオブジェクトがどのように主beanを注入しても、ループ依存の問題を解決することができず、プログラムが起動できません. 主な理由は、主beanオブジェクトがコンストラクション関数を介して依存beanオブジェクトに注入された場合、依存beanオブジェクトを作成することができず、依存beanオブジェクトの参照を取得するためである.下記のコードで示しているからです. メインbeanオブジェクトを作成し、呼び出し順序: コンストラクタを呼び出す.3レベルのキャッシュを配置します.コンストラクション関数を呼び出すと、依存するbeanオブジェクトの作成がトリガーされます. createBeanInstanceは、コンストラクション関数を呼び出してプライマリbeanオブジェクトを作成し、コンストラクション関数に依存するbeanが注入されますが、このときaddSingletonFactoryメソッドは実行されず、プライマリbeanオブジェクトの作成ファクトリを3レベルキャッシュsingletonFactoriesに追加します. したがってcreateBeanInstance内部で、プライマリbeanオブジェクトを注入および作成する際に、コンストラクション関数に他のbeanオブジェクトへの依存が存在し、プライマリbeanオブジェクトへの依存も存在する場合、ループ依存の問題が発生します.原理は次のとおりです.
メインbeanオブジェクトはA、AオブジェクトはBオブジェクトに依存し、BオブジェクトもAオブジェクトに依存している.Aオブジェクトを作成すると、Bオブジェクトの作成がトリガーされ、BはメインbeanオブジェクトAの参照を3段階キャッシュメカニズムで取得できない(すなわち、Bがコンストラクション関数でAを注入した場合、Bオブジェクトを作成できません.属性注入またはsetterメソッドでAを注入した場合、Bオブジェクトを作成した後、Bオブジェクトに属性付与を行うと、populateBeanメソッドにも戻りません)を選択します.したがって、プライマリbeanオブジェクトに依存するBを作成できません.プライマリbeanオブジェクトAを作成すると、createBeanInstanceメソッドが返されず、コードデッドロックが発生し、プログラムレポートのループ依存エラーが発生します.
二、解決できる循環依存
サイクル依存の状況を解決できる
サイクル依存解決の実現
実装ベース
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
// singletonObject
Object singletonObject = this.singletonObjects.get(beanName);
// ,
// , singletonsCurrentlyInCreation
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
// earlySingletonObjects
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
// singletonFactory
ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
//
singletonObject = singletonFactory.getObject();
// earlySingletonObjects
this.earlySingletonObjects.put(beanName, singletonObject);
//
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
インプリメンテーションプロセス
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
//
// beanName singletonsCurrentlyInCreation ,
beforeSingletonCreation(beanName);
try {
// getObject AbstractAutowireCapableBeanFactory createBean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//
}
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
}
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//
// 1. bean , ,
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. singletonFactories , bean ,
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. populateBean :bean
populateBean(beanName, mbd, instanceWrapper);
//
}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
//
// beanName singletonsCurrentlyInCreation ,
beforeSingletonCreation(beanName);
try {
// getObject AbstractAutowireCapableBeanFactory createBean
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//
if (newSingleton) {
// bean , singleObjects
addSingleton(beanName, singletonObject);
}
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
//
this.singletonObjects.put(beanName, singletonObject);
//
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
@RestController
public class AccountController {
private static final Logger LOG = LoggerFactory.getLogger(AccountController.class);
private AccountService accountService;
//
// required true,
@Autowire
// @Autowired(required = false)
public AccountController(AccountService accountService) {
this.accountService = accountService;
}
}
@Service
public class AccountService {
private static final Logger LOG = LoggerFactory.getLogger(AccountService.class);
//
@Autowired
private AccountController accountController;
}
起動印刷は以下の通りである:
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
┌─────┐
| accountController defined in file [/Users/xieyizun/study/personal-projects/easy-web/target/classes/com/yzxie/easy/log/web/controller/AccountController.class]
↑ ↓
| accountService (field private com.yzxie.easy.log.web.controller.AccountController com.yzxie.easy.log.web.service.AccountService.accountController)
└─────┘
@RestController
public class AccountController {
private static final Logger LOG = LoggerFactory.getLogger(AccountController.class);
//
@Autowired
private AccountService accountService;
}
@Service
public class AccountService {
private AccountController accountController;
//
@Autowired
public AccountService(AccountController accountController) {
this.accountController = accountController;
}
}
四、まとめ
// bean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//
// 1. bean , ,
instanceWrapper = createBeanInstance(beanName, mbd, args);
// 2. singletonFactories , bean ,
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
// 3. populateBean :bean
populateBean(beanName, mbd, instanceWrapper);
//
return exposedObject;
}
メインbeanオブジェクトはA、AオブジェクトはBオブジェクトに依存し、BオブジェクトもAオブジェクトに依存している.Aオブジェクトを作成すると、Bオブジェクトの作成がトリガーされ、BはメインbeanオブジェクトAの参照を3段階キャッシュメカニズムで取得できない(すなわち、Bがコンストラクション関数でAを注入した場合、Bオブジェクトを作成できません.属性注入またはsetterメソッドでAを注入した場合、Bオブジェクトを作成した後、Bオブジェクトに属性付与を行うと、populateBeanメソッドにも戻りません)を選択します.したがって、プライマリbeanオブジェクトに依存するBを作成できません.プライマリbeanオブジェクトAを作成すると、createBeanInstanceメソッドが返されず、コードデッドロックが発生し、プログラムレポートのループ依存エラーが発生します.