Spring 5のソースコードを読みます.循環依存をどう解決しますか?
37769 ワード
まず、循環依存は以下のようなものがあることを明確にする.構造関数のサイクル依存性.この依存は明らかに解決できない. は、単一の例ではないビーンの循環依存性である.この依存は解決できない. 単一の例Benのサイクル依存性.本論文で紹介したのは,単一の例のBeanの循環依存性の問題をどのように解決するかである. まずテストコードを見てみます.内容を省略するために、setterとgetterコードを削除します.
次に
次に
方法(1)のタグ1において、この時点でキャッシュに
//Car.java
package beans;
public class Car {
String name;
Person person;
}
//Person.java
package beans;
public class Person {
Car car;
String name;
int age;
}
//Test.java
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Test {
public static void main(String[] args) {
BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml")); // , BeanDefinition , beanFactory 。 bean。
beanFactory.getBean("car"); // getBean() bean
}
}
//beanFactoryTest.xml
"1.0" encoding="UTF-8"?>
"http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
"car" class="beans.Car">
"person" ref="person"/>
"person" class="beans.Person">
"age" value="18"/>
"car" ref="car"/>
"name" value="SJ"/>
ここでまず簡単に説明します.Test
類のmain()
関数では、最初の行のコードはxmlファイルの内容をロードするだけで、いくつかのBeanDefinition
種類を生成し、BeanFactory
のbeanDefinitionMap
に追加し、xmlファイルから直接定義されたbeanを作成していません.getBean()
方法を初めて使用したときに、このbeanおよびその依存するオブジェクト、すなわち怠け者ローディングモードが作成されるのです.getBean()
から入手しましょう.これまでは、beanFactory
の中のいくつかの循環依存性に関する比較的重要なフィールドが、DefaultSingletonBeanRegistry
クラスに定義されています./** Cache of singleton objects: bean name --> bean instance */
private final Map singletonObjects = new ConcurrentHashMap<>(256);
/** Cache of singleton factories: bean name --> ObjectFactory */
private final Map> singletonFactories = new HashMap<>(16);
/** Cache of early singleton objects: bean name --> bean instance */
private final Map earlySingletonObjects = new HashMap<>(16);
/** Names of beans that are currently in creation */
private final Set singletonsCurrentlyInCreation =
Collections.newSetFromMap(new ConcurrentHashMap<>(16));
singletonObjects
は登録されたbeanを保存するためのものであり、中のbeanは初期化されたものである.他の3つは作成中ですが、作成されていないbeanを保存するために使用されます.例えば、Aを作成している間に属性値がBであることが分かり、Aを積み重ねて先にBを初期化しました.しかし、これらの3つにも違いがあります.それらは作成段階の異なるsingletonオブジェクトを保存しています.明らかにsingletonFactories
はbean nameからbean工場へのマッピングです.この工場を通じてname対応のbeanが得られます.他の違いについては以下でゆっくり説明します.皆さん、落ち着いてください.次に
getBean()
関数の解析を開始します.前述したように、bean初期化の開始点です.これからすべてのBeanDefinition
が解析的に生成されたと仮定する(本明細書では2つのbean、Cas、Personが定義されている)が、beanが初期化されたことはなく、Test
種類の第2行が実行される.話は多くなくて、数字を入れますが、紙幅の制限のため、「関係ない」コードをできるだけ削除します.//AbstractBeanFactory.java
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
(1)
protected T doGetBean(final String name, @Nullable final Class requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName); // 1
// ( ), 。
if (sharedInstance != null && args == null) {
//getObjectForBeanInstance() bean FactoryBean , , bean getObject() , 。 ,
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
// ,
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// prototype ,
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// BeanFactory beanName , BeanFactory, BeanFactory
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
// beanName BeanDefinition RootBeanDefinition , bean bean
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
// bean dependsOn 。 , , bean dependsOn , , Car Person ,Person Car 。 , bean, 。
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
getBean(dep);
}
}
// Create bean instance.
if (mbd.isSingleton()) {
// bean
// 2
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
else if (mbd.isPrototype()) {
// bean prototype,blabla...
}
else {
// ,blabla...
}
}
// Check if required type matches the type of the actual bean instance.
//
return (T) bean;
}
getBean()
を初めて呼び出したとき、singletonObjects
、singletonFactories
などの4つのフィールドは全部空です.方法(1)のマーク1にあるgetSingleton()
のコードをもう一度見てください.//DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName) {
return getSingleton(beanName, true);
}
(2)
protected Object getSingleton(String beanName, boolean allowEarlyReference) {
Object singletonObject = this.singletonObjects.get(beanName);
//isSingletonCurrentlyInCreation singletonsCurrentlyInCreation bean, , bean
if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
synchronized (this.singletonObjects) {
singletonObject = this.earlySingletonObjects.get(beanName);
if (singletonObject == null && allowEarlyReference) {
ObjectFactory> singletonFactory = this.singletonFactories.get(beanName);
if (singletonFactory != null) {
singletonObject = singletonFactory.getObject(); // 1
this.earlySingletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
}
}
}
}
return singletonObject;
}
この場合、いくつかのフィールドが空ですので、方法(2)は直接nullに戻ります.方法(1)に戻り、次の比較的重要な方法は、方法(1)マーク2におけるコードであり、このときgetSingleton()
コードは以下の通りである.//DefaultSingletonBeanRegistry.java
(3)
public Object getSingleton(String beanName, ObjectFactory> singletonFactory) {
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// bean singletonsCurrentlyInCreation
beforeSingletonCreation(beanName);
boolean newSingleton = false;
try {
// getObject() !! !! , bean , bean !! , , 。
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
finally {
// , bean singletonsCurrentlyInCreation
afterSingletonCreation(beanName);
}
if (newSingleton) {
// bean singletonObjects , singletonObjects bean
addSingleton(beanName, singletonObject);
}
}
return singletonObject;
}
}
方法(3)に渡すパラメータは、方法(1)のフラグ2においてcreateBean(beanName, mbd, args)
であり、この関数に入る.protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// bean
if (logger.isDebugEnabled()) {
logger.debug("Creating instance of bean '" + beanName + "'");
}
RootBeanDefinition mbdToUse = mbd;
// Make sure bean class is actually resolved at this point, and
// clone the bean definition in case of a dynamically resolved Class
// which cannot be stored in the shared merged bean definition.
Class> resolvedClass = resolveBeanClass(mbd, beanName);
if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
mbdToUse = new RootBeanDefinition(mbd);
mbdToUse.setBeanClass(resolvedClass);
}
// Prepare method overrides.
// ,
try {
mbdToUse.prepareMethodOverrides();
}
try {
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
// InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation 。 bean == null。
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) { // bean null, ,
return bean;
}
}
try {
//doCreateBean bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isDebugEnabled()) {
logger.debug("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
}
doCreateBean()
関数を見てみます.この関数はまずBeanWrapper
類のオブジェクトを構成し、対応するbeanを包装した後、BeanWrapper.getWrappedInstance()
方法を呼び出して、包装されたbeanを得ることができます.//AbstractAutowireCapableBeanFactory.java
(4)
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// BeanWrapper ,
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
// BeanWrapper bean
final Object bean = instanceWrapper.getWrappedInstance();
Class> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}
// Allow post-processors to modify the merged bean definition.
// MergedBeanDefinitionPostProcessor
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
mbd.postProcessed = true;
}
}
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
// bean , populateBean() , bean
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
// getBean() bean, bean BeanFactory singletonFactories
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}
// Initialize the bean instance.
Object exposedObject = bean;
try {
// bean , bean, bean
populateBean(beanName, mbd, instanceWrapper);
// , init-method, bean Aware , BeanFactoryAware, bean
exposedObject = initializeBean(beanName, exposedObject, mbd); // 1
}
if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] 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. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}
return exposedObject;
}
循環依存性の問題はpopulateBean()
関数で行われるので、この関数のコードを見てみましょう.//AbstractAutowireCapableBeanFactory.java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
// state of the bean before properties are set. This can be used, for example,
// to support styles of field injection.
boolean continueWithPropertyPopulation = true;
// InstantiationAwareBeanPostProcessor
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
// false InstantiationAwareBeanPostProcessor , bean
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
if (!continueWithPropertyPopulation) {
return;
}
// BeanDefinition
PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
// xml , mbd.getResolvedAutowireMode() = 0, 。 ,autowireByName autowireByType bean , getBean() bean
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
// Add property values based on autowire by name if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
autowireByName(beanName, mbd, bw, newPvs);
}
// Add property values based on autowire by type if applicable.
if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
autowireByType(beanName, mbd, bw, newPvs);
}
pvs = newPvs;
}
if (pvs != null) {
applyPropertyValues(beanName, mbd, bw, pvs);
}
}
はい、属性値を設定するということは、実はapplyPropertyValues
関数にあります.//AbstractAutowireCapableBeanFactory.java
(5)
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {
if (pvs.isEmpty()) {
return;
}
MutablePropertyValues mpvs = null;
List original;
if (pvs instanceof MutablePropertyValues) {
mpvs = (MutablePropertyValues) pvs;
if (mpvs.isConverted()) {
// Shortcut: use the pre-converted values as-is.
try {
bw.setPropertyValues(mpvs);
return;
}
}
original = mpvs.getPropertyValueList();
}
else {
original = Arrays.asList(pvs.getPropertyValues());
}
TypeConverter converter = getCustomTypeConverter();
if (converter == null) {
converter = bw;
}
//
BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter);
// Create a deep copy, resolving any references for values.
List deepCopy = new ArrayList<>(original.size());
boolean resolveNecessary = false;
for (PropertyValue pv : original) {
if (pv.isConverted()) {
deepCopy.add(pv);
}
else {
String propertyName = pv.getName();
Object originalValue = pv.getValue();
// ref bean bean, , getBean()
Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);
Object convertedValue = resolvedValue;
boolean convertible = bw.isWritableProperty(propertyName) &&
!PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName);
if (convertible) {
convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter);
}
// Possibly store converted value in merged bean definition,
// in order to avoid re-conversion for every created bean instance.
if (resolvedValue == originalValue) {
if (convertible) {
pv.setConvertedValue(convertedValue);
}
deepCopy.add(pv);
}
else if (convertible && originalValue instanceof TypedStringValue &&
!((TypedStringValue) originalValue).isDynamic() &&
!(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) {
pv.setConvertedValue(convertedValue);
deepCopy.add(pv);
}
else {
resolveNecessary = true;
deepCopy.add(new PropertyValue(pv, convertedValue));
}
}
}
if (mpvs != null && !resolveNecessary) {
mpvs.setConverted();
}
// Set our (possibly massaged) deep copy.
try {
//
bw.setPropertyValues(new MutablePropertyValues(deepCopy));
}
}
resolveValueIfNecessary
方法では、
タグのref
値に対応する論理は、属性値の種類に応じて、resolveReference
メソッドを呼び出すことであり、この方法でgetBean()
メソッドを呼び出して、依存する処理を完了することである.ここで、まとめてみると、car
オブジェクトの初期化はまだ完了していないので、getBean(person)
メソッドを呼び出してperson
オブジェクトを得る必要があり、person
オブジェクトをcar
オブジェクトの属性に注入することができる.これまでcar
オブジェクトは、singletonFactories
キャッシュに自分を追加しました.次に
getBean("person")
のプロセスであり、getBean("car")
と同様に、方法(5)についてもperson
のオブジェクトの属性値を設定する必要があるので、次にgetBean("car")
の呼び出しが発生したが、今回はgetBean("car")
の第2回の呼び出しであり、第1回とどのような違いがあるかを見てみよう.方法(1)のタグ1において、この時点でキャッシュに
car
のバーがあり、方法(2)のタグ1においてcar
に対応する工場関数を呼び出し、この関数においてキャッシュされたcar
を取得し、他のキャッシュearlySingletonObjects
に追加し、その工場関数をキャッシュsingletonFactories
から削除する.その後、方法(1)の流れに戻り、キャッシュ中のオブジェクトを取得した後、再度作成する必要はなく、直接getObjectForBeanInstance
の呼び出しを行い(前に述べたように、FactoryBenインターフェースを処理するためのオブジェクトに過ぎない)、その後、タイプ検査を行って直接person
に戻すことができます.最後にperson
の方法(5)では、person
に属性として設定される.これでperson
の作成が完了し、car
に戻る方法(5).しかし、戻る前に、方法(4)のフラグ1において、initializeBean()
方法が呼び出され、ここでビーンの初期化関数およびポストプロセッサの方法が呼び出された.その後、方法(3)においてperson
をsingletonsCurrentlyInCreation
およびearlySingletonObjects
から削除し、person
をsingletonObjects
キャッシュに追加し、次にgetBean("person")
を呼び出すと、直接singletonObjects
キャッシュから取得することができる.person
をcar
に戻す方法(5)の後も同様の流れであり、初期化方法と後プロセッサの方法を呼び出し、次いでcar
を永久キャッシュsingletonObjects
に追加し、一時キャッシュearlySingletonObjects
から削除して、これで2つの循環依存関係があるbeanは初期化された.